JavaScript: concise texture creation functions

This makes our createTexture* helpers more consistent with the
createMaterial helper, and extends the wrapped Engine class with new
methods.

Before, clients did something like this:

    const ao = Filament.createTextureFromPng(Filament.assets['foo.png'], engine);
    ...
    engine.destroyTexture(ao);

Now, they can do:

    const ao = engine.createTextureFromPng('foo.png');
    ...
    engine.destroyTexture(ao);

This is more symmetric and concise. In general the JS API now supports a
class of "buffer-like" objects which exploit dynamic typing in order to
accept any of the following: BufferDescriptor, asset string, or
Uint8Array.
This commit is contained in:
Philip Rideout
2018-10-29 18:09:45 -07:00
parent 0deafea0da
commit 5da454f652
9 changed files with 138 additions and 75 deletions
+1
View File
@@ -4,6 +4,7 @@ project(filamentjs)
set(JAVASCRIPT_SRC
${CMAKE_CURRENT_SOURCE_DIR}/wasmloader.js
${CMAKE_CURRENT_SOURCE_DIR}/extensions.js
${CMAKE_CURRENT_SOURCE_DIR}/utilities.js)
set(CPP_SRC
+10
View File
@@ -278,6 +278,15 @@ def gather_docstrings(paths):
"detail": None,
"children": []
}
# Check if this is continuation of a previous type.
if brief== '':
for existing_type in result:
if existing_type['name'] == name:
entity = existing_type
result.remove(existing_type)
break
top = stack[-1]["tags"]
if 'root' in top:
result.append(entity)
@@ -436,6 +445,7 @@ def build_reference():
ROOT_DIR + 'libs/filamentjs/jsenums.cpp',
ROOT_DIR + 'libs/filamentjs/utilities.js',
ROOT_DIR + 'libs/filamentjs/wasmloader.js',
ROOT_DIR + 'libs/filamentjs/extensions.js',
])
markdown = build_reference_markdown(doctree)
rendered = mistletoe.markdown(markdown, PygmentsRenderer)
+3 -7
View File
@@ -139,8 +139,7 @@ Next let's create a material instance from the package that we built at the begi
Replace the **create material** comment with the following snippet.
```js {fragment="create material"}
const material_package = Filament.Buffer(Filament.assets['plastic.filamat']);
const material = engine.createMaterial(material_package);
const material = engine.createMaterial('plastic.filamat');
const matinstance = material.createInstance();
const red = [0.8, 0.0, 0.0];
@@ -269,8 +268,7 @@ Filament provides a JavaScript utility to make this simpler,
simply replace the **create IBL** comment with the following snippet.
```js {fragment="create IBL"}
const ibldata = Filament.assets['pillars_2k_ibl.ktx'];
const indirectLight = Filament.createIblFromKtx(ibldata, engine, {'rgbm': true});
const indirectLight = engine.createIblFromKtx('pillars_2k_ibl.ktx');
indirectLight.setIntensity(50000);
scene.setIndirectLight(indirectLight);
```
@@ -302,9 +300,7 @@ Filament provides a Javascript utility to make this easier.
Replace **create skybox** with the following.
```js {fragment="create skybox"}
const skydata = Filament.assets['pillars_2k_skybox.ktx'];
const skytex = Filament.createTextureFromKtx(skydata, engine, {'rgbm': true});
const skybox = Filament.Skybox.Builder().environment(skytex).build(engine);
const skybox = engine.createSkyFromKtx('pillars_2k_skybox.ktx');
scene.setSkybox(skybox);
```
+1 -3
View File
@@ -135,8 +135,6 @@ const TRIANGLE_COLORS = Filament.Buffer(new Uint32Array([
0xff00ff00,
0xff0000ff,
]));
const MATERIAL_PACKAGE = Filament.Buffer(Filament.assets['triangle.filamat']);
```
Next we'll use the positions and colors buffers to create a single `VertexBuffer` object.
@@ -195,7 +193,7 @@ After extracting the material instance, we can finally create a renderable compo
triangle by setting up a bounding box and passing in the vertex and index buffers.
```js {fragment="create entities"}
const mat = engine.createMaterial(MATERIAL_PACKAGE);
const mat = engine.createMaterial('triangle.filamat');
const matinst = mat.getDefaultInstance();
Filament.RenderableManager.Builder(1)
.boundingBox([[ -1, -1, -1 ], [ 1, 1, 1 ]])
+100
View File
@@ -0,0 +1,100 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Private utility that converts an asset string or Uint8Array into a low-level buffer descriptor.
function getBufferDescriptor(buffer) {
if ('string' == typeof buffer || buffer instanceof String) {
buffer = Filament.assets[buffer];
}
if (buffer instanceof Uint8Array) {
buffer = Filament.Buffer(buffer);
}
return buffer;
}
Filament.loadClassExtensions = function() {
/// Engine ::core class::
/// create ::static method::
/// canvas ::argument::
/// options ::argument::
/// ::retval:: an instance of [Engine]
Filament.Engine.create = function(canvas, options) {
const defaults = {
majorVersion: 2,
minorVersion: 0,
antialias: false,
depth: false,
alpha: false
};
options = Object.assign(defaults, options);
Filament.createContext(canvas, true, true, options);
return Filament.Engine._create();
};
/// createMaterial ::method::
/// package ::argument:: asset string, or Uint8Array, or [Buffer] with filamat contents
/// ::retval:: an instance of [createMaterial]
Filament.Engine.prototype.createMaterial = function(buffer) {
return this._createMaterial(getBufferDescriptor(buffer));
};
/// createTextureFromKtx ::method:: Utility function that creates a [Texture] from a KTX file.
/// buffer ::argument:: asset string, or Uint8Array, or [Buffer] with KTX file contents
/// options ::argument:: Options dictionary. For now, the `rgbm` boolean is the only option.
/// ::retval:: [Texture]
Filament.Engine.prototype.createTextureFromKtx = function(buffer, options) {
return Filament._createTextureFromKtx(getBufferDescriptor(buffer), this, options);
};
/// createIblFromKtx ::method:: Utility function that creates an [IndirectLight] from a KTX file.
/// buffer ::argument:: asset string, or Uint8Array, or [Buffer] with KTX file contents
/// options ::argument:: Options dictionary. For now, the `rgbm` boolean is the only option.
/// ::retval:: [IndirectLight]
Filament.Engine.prototype.createIblFromKtx = function(buffer, options) {
return Filament._createIblFromKtx(getBufferDescriptor(buffer), this, options);
}
/// createSkyFromKtx ::method:: Utility function that creates a [Skybox] from a KTX file.
/// buffer ::argument:: asset string, or Uint8Array, or [Buffer] with KTX file contents
/// options ::argument:: Options dictionary. For now, the `rgbm` boolean is the only option.
/// ::retval:: [Skybox]
Filament.Engine.prototype.createSkyFromKtx = function(buffer, options) {
options = options || {'rgbm': true};
const skytex = this.createTextureFromKtx(buffer, options);
return Filament.Skybox.Builder().environment(skytex).build(this);
};
/// createTextureFromPng ::method:: Creates a 2D [Texture] from the raw contents of a PNG file.
/// buffer ::argument:: asset string, or Uint8Array, or [Buffer] with PNG file contents
/// options ::argument:: JavaScript object with optional `rgbm`, `noalpha`, and `nomips` keys.
/// ::retval:: [Texture]
Filament.Engine.prototype.createTextureFromPng = function(buffer, options) {
return Filament._createTextureFromPng(getBufferDescriptor(buffer), this, options);
}
/// loadFilamesh ::method:: Consumes the contents of a filamesh file and creates a renderable.
/// buffer ::argument:: asset string, or Uint8Array, or [Buffer] with filamesh contents
/// definstance ::argument:: Optional default [MaterialInstance]
/// matinstances ::argument:: Optional object that gets populated with name => [MaterialInstance]
/// ::retval:: JavaScript object with keys `renderable`, `vertexBuffer`, and `indexBuffer`. \
/// These are of type [Entity], [VertexBuffer], and [IndexBuffer].
Filament.Engine.prototype.loadFilamesh = function(buffer, definstance, matinstances) {
return Filament._loadFilamesh(this, getBufferDescriptor(buffer), definstance, matinstances);
}
};
+2 -7
View File
@@ -260,7 +260,7 @@ value_array<flatmat3>("mat3")
// CORE FILAMENT CLASSES
// ---------------------
/// Engine ::core class:: Central manager and resource owner. Typically this is a singleton.
/// Engine ::core class:: Central manager and resource owner.
class_<Engine>("Engine")
.class_function("_create", (Engine* (*)()) [] { return Engine::create(); },
allow_raw_pointers())
@@ -317,12 +317,7 @@ class_<Engine>("Engine")
.function("destroyCamera", (void (*)(Engine*, Camera*)) []
(Engine* engine, Camera* camera) { engine->destroy(camera); },
allow_raw_pointers())
/// createMaterial ::method::
/// package ::argument:: buffer descriptor created with [Buffer] with filamat contents
/// ::retval:: an instance of [createMaterial]
.function("createMaterial", EMBIND_LAMBDA(Material*, (Engine* engine, BufferDescriptor mbd), {
// TODO: consider adding a BufferDescriptor API to Material::Builder, then possibly removing
// this convenient helper method.
.function("_createMaterial", EMBIND_LAMBDA(Material*, (Engine* engine, BufferDescriptor mbd), {
return Material::Builder().package(mbd.bd->buffer, mbd.bd->size).build(*engine);
}), allow_raw_pointers())
/// destroyMaterial ::method::
+9 -14
View File
@@ -37,21 +37,19 @@ class App {
.build(engine, sunlight);
this.scene.addEntity(sunlight);
const ibldata = Filament.assets['venetian_crossroads_2k_ibl.ktx'];
const indirectLight = this.ibl = Filament.createIblFromKtx(ibldata, engine, {'rgbm': true});
const ibldata = 'venetian_crossroads_2k_ibl.ktx';
const indirectLight = this.ibl = engine.createIblFromKtx(ibldata);
this.scene.setIndirectLight(indirectLight);
const radians = 1.0;
indirectLight.setRotation(mat3.fromRotation(mat3.create(), radians, [0, 1, 0]))
indirectLight.setIntensity(10000);
const skydata = Filament.assets['venetian_crossroads_2k_skybox.ktx'];
const skytex = Filament.createTextureFromKtx(skydata, engine, {'rgbm': true});
const skybox = Filament.Skybox.Builder().environment(skytex).build(engine);
const skydata = 'venetian_crossroads_2k_skybox.ktx';
const skybox = engine.createSkyFromKtx(skydata);
this.scene.setSkybox(skybox);
const MATERIAL_PACKAGE = Filament.Buffer(Filament.assets['parquet.filamat']);
const material = engine.createMaterial(MATERIAL_PACKAGE);
const material = engine.createMaterial('parquet.filamat');
const matinstance = material.createInstance();
const sampler = new Filament.TextureSampler(
@@ -59,17 +57,14 @@ class App {
Filament.MagFilter.LINEAR,
Filament.WrapMode.CLAMP_TO_EDGE);
const ao = Filament.createTextureFromPng(
Filament.assets['floor_ao_roughness_metallic.png'], engine);
const basecolor = Filament.createTextureFromPng(
Filament.assets['floor_basecolor.png'], engine, {'srgb': true});
const normal = Filament.createTextureFromPng(Filament.assets['floor_normal.png'], engine);
const ao = engine.createTextureFromPng('floor_ao_roughness_metallic.png');
const basecolor = engine.createTextureFromPng('floor_basecolor.png', {'srgb': true});
const normal = engine.createTextureFromPng('floor_normal.png');
matinstance.setTextureParameter('aoRoughnessMetallic', ao, sampler)
matinstance.setTextureParameter('baseColor', basecolor, sampler)
matinstance.setTextureParameter('normal', normal, sampler)
const meshdata = Filament.assets['shader_ball.filamesh'];
const mesh = Filament.loadFilamesh(engine, meshdata, matinstance);
const mesh = engine.loadFilamesh('shader_ball.filamesh', matinstance);
this.shaderball = mesh.renderable;
this.scene.addEntity(mesh.renderable);
+11 -32
View File
@@ -59,14 +59,7 @@ Filament.PixelBuffer = function(typedarray, format, datatype) {
return bd;
};
/// loadFilamesh ::function:: Consumes the contents of a filamesh file and creates a renderable.
/// engine ::argument:: [Engine]
/// typedarray ::argument:: Uint8Array with contents of filamesh file
/// definstance ::argument:: Optional default [MaterialInstance]
/// matinstances ::argument:: Optional object that gets populated with name => [MaterialInstance]
/// ::retval:: JavaScript object with keys `renderable`, `vertexBuffer`, and `indexBuffer`. \
/// These are of type [Entity], [VertexBuffer], and [IndexBuffer].
Filament.loadFilamesh = function(engine, typedarray, definstance, matinstances) {
Filament._loadFilamesh = function(engine, buffer, definstance, matinstances) {
matinstances = matinstances || {};
const registry = new Filament.MeshIO$MaterialRegistry();
for (var key in matinstances) {
@@ -75,7 +68,7 @@ Filament.loadFilamesh = function(engine, typedarray, definstance, matinstances)
if (definstance) {
registry.set("DefaultMaterial", definstance);
}
const mesh = Filament.MeshIO.loadMeshFromBuffer(engine, Filament.Buffer(typedarray), registry);
const mesh = Filament.MeshIO.loadMeshFromBuffer(engine, buffer, registry);
const keys = registry.keys();
for (var i = 0; i < keys.size(); i++) {
const key = keys.get(i);
@@ -223,12 +216,7 @@ Filament.loadMathExtensions = function() {
// Texture helpers
// ---------------
/// createTextureFromKtx ::function:: Utility function that creates a [Texture] from a KTX file.
/// ktxdata ::argument:: Uint8Array with the contents of a KTX file.
/// engine ::argument:: [Engine]
/// options ::argument:: Options dictionary. For now, the `rgbm` boolean is the only option.
/// ::retval:: [Texture]
Filament.createTextureFromKtx = function(ktxdata, engine, options) {
Filament._createTextureFromKtx = function(ktxdata, engine, options) {
options = options || {};
const Sampler = Filament.Texture$Sampler;
@@ -236,7 +224,7 @@ Filament.createTextureFromKtx = function(ktxdata, engine, options) {
const PixelDataFormat = Filament.PixelDataFormat;
const gl = Filament.ctx;
const ktx = options['ktx'] || new Filament.KtxBundle(Filament.Buffer(ktxdata));
const ktx = options['ktx'] || new Filament.KtxBundle(ktxdata);
const nlevels = ktx.getNumMipLevels();
const ktxformat = ktx.info().glInternalFormat;
const rgbm = !!options['rgbm'];
@@ -285,15 +273,10 @@ Filament.createTextureFromKtx = function(ktxdata, engine, options) {
return tex;
};
/// createIblFromKtx ::function:: Utility function that creates an [IndirectLight] from a KTX file.
/// ktxdata ::argument:: Uint8Array with the contents of a KTX file.
/// engine ::argument:: [Engine]
/// options ::argument:: Options dictionary. For now, the `rgbm` boolean is the only option.
/// ::retval:: [IndirectLight]
Filament.createIblFromKtx = function(ktxdata, engine, options) {
options = options || {};
const iblktx = options['ktx'] = new Filament.KtxBundle(Filament.Buffer(ktxdata));
const ibltex = Filament.createTextureFromKtx(ktxdata, engine, options);
Filament._createIblFromKtx = function(ktxdata, engine, options) {
options = options || {'rgbm': true};
const iblktx = options['ktx'] = new Filament.KtxBundle(ktxdata);
const ibltex = Filament._createTextureFromKtx(ktxdata, engine, options);
const shstring = iblktx.getMetadata("sh");
const shfloats = shstring.split(/\s/, 9 * 3).map(parseFloat);
return Filament.IndirectLight.Builder()
@@ -302,12 +285,7 @@ Filament.createIblFromKtx = function(ktxdata, engine, options) {
.build(engine);
};
/// createTextureFromPng ::function:: Creates a 2D [Texture] from the raw contents of a PNG file.
/// pngdata ::argument:: Uint8Array with the contents of a PNG file.
/// engine ::argument:: [Engine]
/// options ::argument:: JS object with optional `srgb`, `rgbm`, `noalpha`, and `nomips` keys.
/// ::retval:: [Texture]
Filament.createTextureFromPng = function(pngdata, engine, options) {
Filament._createTextureFromPng = function(pngdata, engine, options) {
const Sampler = Filament.Texture$Sampler;
const TextureFormat = Filament.Texture$InternalFormat;
const PixelDataFormat = Filament.PixelDataFormat;
@@ -317,7 +295,8 @@ Filament.createTextureFromPng = function(pngdata, engine, options) {
const rgbm = !!options['rgbm'];
const noalpha = !!options['noalpha'];
const nomips = !!options['nomips'];
const decodedpng = Filament.decodePng(Filament.Buffer(pngdata), noalpha ? 3 : 4);
const decodedpng = Filament.decodePng(pngdata, noalpha ? 3 : 4);
var texformat, pbformat, pbtype;
if (noalpha) {
+1 -12
View File
@@ -77,18 +77,7 @@ Filament.init = function(assets, onready) {
// WebAssembly module. The JS classes that correspond to core Filament classes (e.g., Engine)
// are not guaranteed to exist until this function is called.
Filament.postRun = function() {
Filament.Engine.create = function(canvas, options) {
const defaults = {
majorVersion: 2,
minorVersion: 0,
antialias: false,
depth: false,
alpha: false
};
options = Object.assign(defaults, options);
Filament.createContext(canvas, true, true, options);
return Filament.Engine._create();
};
Filament.loadClassExtensions();
if (--Filament.remainingInitializationTasks == 0 && Filament.onReady) {
Filament.onReady();
}