You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
466 lines
12 KiB
JavaScript
466 lines
12 KiB
JavaScript
import { defined, RuntimeError } from "../../Source/Cesium.js";
|
|
import findAccessorMinMax from "../../Source/ThirdParty/GltfPipeline/findAccessorMinMax.js";
|
|
|
|
/**
|
|
* A fluent interface for programmatically building a glTF.
|
|
* @alias GltfBuilder
|
|
* @constructor
|
|
* @private
|
|
*/
|
|
function GltfBuilder() {
|
|
this.gltf = {
|
|
asset: {
|
|
generator: "cesium-tests",
|
|
version: "2.0",
|
|
},
|
|
extensionsUsed: [],
|
|
accessors: [],
|
|
buffers: [],
|
|
bufferViews: [],
|
|
materials: [],
|
|
meshes: [],
|
|
nodes: [
|
|
{
|
|
mesh: 0,
|
|
},
|
|
],
|
|
scenes: [
|
|
{
|
|
nodes: [0],
|
|
},
|
|
],
|
|
scene: 0,
|
|
};
|
|
|
|
this.bufferBuilders = [];
|
|
}
|
|
|
|
/**
|
|
* Creates a new buffer.
|
|
* @param {string} [name] The name of the buffer.
|
|
* @returns {GltfBufferBuilder}
|
|
*/
|
|
GltfBuilder.prototype.buffer = function (name) {
|
|
var index =
|
|
this.gltf.buffers.push({
|
|
name: name,
|
|
byteLength: 0,
|
|
}) - 1;
|
|
var bufferBuilder = new GltfBufferBuilder(this, index);
|
|
this.bufferBuilders.push(bufferBuilder);
|
|
return bufferBuilder;
|
|
};
|
|
|
|
/**
|
|
* Creates a new mesh.
|
|
* @param {string} [name] The name of the mesh.
|
|
* @returns {GltfMeshBuilder}
|
|
*/
|
|
GltfBuilder.prototype.mesh = function (name) {
|
|
var index =
|
|
this.gltf.meshes.push({
|
|
name: name,
|
|
primitives: [],
|
|
}) - 1;
|
|
|
|
var meshBuilder = new GltfMeshBuilder(this, index);
|
|
return meshBuilder;
|
|
};
|
|
|
|
/**
|
|
* Creates a new material.
|
|
* @param {string} [name] The name of the material.
|
|
* @returns {GltfMaterialBuilder}
|
|
*/
|
|
GltfBuilder.prototype.material = function (name) {
|
|
var index =
|
|
this.gltf.materials.push({
|
|
name: name,
|
|
}) - 1;
|
|
var materialBuilder = new GltfMaterialBuilder(this, index);
|
|
return materialBuilder;
|
|
};
|
|
|
|
/**
|
|
* Gets the built glTF JSON from this builder. Calling this a second
|
|
* time will cause the glTF returned in the first call to be invalidated.
|
|
* Specifically, the `uri` properties of its buffers will no longer be
|
|
* resolvable.
|
|
*
|
|
* After calling this method, be sure to call {@link GltfBuilder#destroy}
|
|
* when done with the glTF, to a void leaking buffer memory.
|
|
*/
|
|
GltfBuilder.prototype.toGltf = function () {
|
|
for (var i = 0; i < this.bufferBuilders.length; ++i) {
|
|
var bufferBuilder = this.bufferBuilders[i];
|
|
|
|
var byteLength = bufferBuilder.viewBuilders.reduce(function (
|
|
byteLength,
|
|
viewBuilder
|
|
) {
|
|
return byteLength + viewBuilder.bufferView.byteLength;
|
|
},
|
|
0);
|
|
|
|
var buffer = new ArrayBuffer(byteLength);
|
|
var nextStart = 0;
|
|
|
|
for (var j = 0; j < bufferBuilder.viewBuilders.length; ++j) {
|
|
var viewBuilder = bufferBuilder.viewBuilders[j];
|
|
viewBuilder.bufferView.byteOffset = nextStart;
|
|
var destBuffer =
|
|
viewBuilder.componentType === 5126
|
|
? new Float32Array(buffer, nextStart, viewBuilder._data.length)
|
|
: new Uint16Array(buffer, nextStart, viewBuilder._data.length);
|
|
destBuffer.set(viewBuilder._data);
|
|
nextStart += viewBuilder.bufferView.byteLength;
|
|
}
|
|
|
|
bufferBuilder.buffer.byteLength = byteLength;
|
|
|
|
if (bufferBuilder.buffer.uri) {
|
|
URL.revokeObjectURL(bufferBuilder.buffer.uri);
|
|
}
|
|
|
|
bufferBuilder.buffer.uri = URL.createObjectURL(new Blob([buffer]));
|
|
|
|
bufferBuilder.buffer.extras = {
|
|
_pipeline: {
|
|
source: new Uint8Array(buffer, 0, buffer.byteLength),
|
|
},
|
|
};
|
|
}
|
|
|
|
var gltf = this.gltf;
|
|
gltf.accessors.forEach(function (accessor) {
|
|
var minMax = findAccessorMinMax(gltf, accessor);
|
|
accessor.min = minMax.min;
|
|
accessor.max = minMax.max;
|
|
});
|
|
|
|
this.bufferBuilders.forEach(function (builder) {
|
|
delete builder.buffer.extras;
|
|
});
|
|
|
|
return this.gltf;
|
|
};
|
|
|
|
/**
|
|
* Frees memory allocated for buffers in {@link GltfBuilder@toGltf}.
|
|
*/
|
|
GltfBuilder.prototype.destroy = function () {
|
|
this.bufferBuilders.forEach(function (bufferBuilder) {
|
|
URL.revokeObjectURL(bufferBuilder.buffer.uri);
|
|
bufferBuilder.buffer.uri = undefined;
|
|
});
|
|
};
|
|
|
|
/**
|
|
* A fluent interface for programmatically building a glTF material.
|
|
* @param {GltfBuilder} gltfBuilder The glTF builder.
|
|
* @param {number} materialIndex The index of this material within the glTF.
|
|
* @private
|
|
*/
|
|
function GltfMaterialBuilder(gltfBuilder, materialIndex) {
|
|
this.gltfBuilder = gltfBuilder;
|
|
this.materialIndex = materialIndex;
|
|
this.material = this.gltfBuilder.gltf.materials[materialIndex];
|
|
}
|
|
|
|
/**
|
|
* Defines the material using JSON.
|
|
* @param {*} json The JSON definition of the material.
|
|
* @returns {GltfMaterialBuilder}
|
|
*/
|
|
GltfMaterialBuilder.prototype.json = function (json) {
|
|
for (var property in json) {
|
|
if (!json.hasOwnProperty(property)) {
|
|
continue;
|
|
}
|
|
|
|
this.material[property] = json[property];
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* A fluent interface for building a glTF mesh.
|
|
* @param {GltfBuilder} gltfBuilder The glTF builder.
|
|
* @param {number} meshIndex The index of this mesh within the glTF.
|
|
* @private
|
|
*/
|
|
function GltfMeshBuilder(gltfBuilder, meshIndex) {
|
|
this.gltfBuilder = gltfBuilder;
|
|
this.meshIndex = meshIndex;
|
|
this.mesh = gltfBuilder.gltf.meshes[this.meshIndex];
|
|
}
|
|
|
|
/**
|
|
* Creates a new primitive within this mesh.
|
|
* @param {string} [name] The name of the primitive.
|
|
* @returns {GltfPrimitiveBuilder}
|
|
*/
|
|
GltfMeshBuilder.prototype.primitive = function (name) {
|
|
var index =
|
|
this.mesh.primitives.push({
|
|
name: name,
|
|
attributes: {},
|
|
}) - 1;
|
|
|
|
var meshBuilder = new GltfPrimitiveBuilder(this, index);
|
|
return meshBuilder;
|
|
};
|
|
|
|
/**
|
|
* A fluent interface for building a glTF primitive.
|
|
* @param {GltfMeshBuilder} gltfMeshBuilder The mesh builder.
|
|
* @param {number} primitiveIndex The index of this primitive within the mesh.
|
|
* @private
|
|
*/
|
|
function GltfPrimitiveBuilder(gltfMeshBuilder, primitiveIndex) {
|
|
this.gltfMeshBuilder = gltfMeshBuilder;
|
|
this.primitiveIndex = primitiveIndex;
|
|
this.primitive = this.gltfMeshBuilder.mesh.primitives[this.primitiveIndex];
|
|
}
|
|
|
|
/**
|
|
* Adds a new attribute to the primitive.
|
|
* @param {string} semantic The semantic of the attribute.
|
|
* @param {string} accessorName The name of the accessor referenced by this attribute.
|
|
* @returns {GltfPrimitiveBuilder}
|
|
*/
|
|
GltfPrimitiveBuilder.prototype.attribute = function (semantic, accessorName) {
|
|
var gltf = this.gltfMeshBuilder.gltfBuilder.gltf;
|
|
var accessorId = findAccessorByName(gltf, accessorName);
|
|
if (accessorId < 0) {
|
|
throw new RuntimeError("Accessor named " + accessorName + " not found.");
|
|
}
|
|
|
|
this.primitive.attributes[semantic] = accessorId;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Sets the name of the accessor providing this primitive's indices.
|
|
* @param {string} accessorName The name of the accessor providing the indices.
|
|
* @returns {GltfPrimitiveBuilder}
|
|
*/
|
|
GltfPrimitiveBuilder.prototype.indices = function (accessorName) {
|
|
var gltf = this.gltfMeshBuilder.gltfBuilder.gltf;
|
|
var accessorId = findAccessorByName(gltf, accessorName);
|
|
if (accessorId < 0) {
|
|
throw new RuntimeError("Accessor named " + accessorName + " not found.");
|
|
}
|
|
|
|
this.primitive.indices = accessorId;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Indicates that this primitive is TRIANGLES.
|
|
* @returns {GltfPrimitiveBuilder}
|
|
*/
|
|
GltfPrimitiveBuilder.prototype.triangles = function () {
|
|
this.primitive.mode = 4;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Sets the material applied to this primitive.
|
|
* @param {string} materialName The name of the material.
|
|
* @returns {GltfPrimitiveBuilder}
|
|
*/
|
|
GltfPrimitiveBuilder.prototype.material = function (materialName) {
|
|
var gltf = this.gltfMeshBuilder.gltfBuilder.gltf;
|
|
|
|
for (var i = 0; i < gltf.materials.length; ++i) {
|
|
if (gltf.materials[i].name === materialName) {
|
|
this.primitive.material = i;
|
|
return this;
|
|
}
|
|
}
|
|
|
|
throw new RuntimeError("Material named " + materialName + " not found.");
|
|
};
|
|
|
|
/**
|
|
* A fluent interface for building a glTF buffer.
|
|
* @param {GltfBuilder} gltfBuilder The glTF builder.
|
|
* @param {number} bufferIndex The index of this buffer in the glTF.
|
|
* @private
|
|
*/
|
|
function GltfBufferBuilder(gltfBuilder, bufferIndex) {
|
|
this.gltfBuilder = gltfBuilder;
|
|
this.bufferIndex = bufferIndex;
|
|
this.buffer = this.gltfBuilder.gltf.buffers[bufferIndex];
|
|
this.viewBuilders = [];
|
|
}
|
|
|
|
/**
|
|
* Creates a new vertex bufferView in this buffer.
|
|
* @param {string} name The name of the bufferView.
|
|
* @returns {GltfBufferViewBuilder}
|
|
*/
|
|
GltfBufferBuilder.prototype.vertexBuffer = function (name) {
|
|
var index =
|
|
this.gltfBuilder.gltf.bufferViews.push({
|
|
name: name,
|
|
buffer: this.bufferIndex,
|
|
byteLength: 0,
|
|
target: 34962,
|
|
}) - 1;
|
|
var viewBuilder = new GltfBufferViewBuilder(this, index, 5126);
|
|
this.viewBuilders.push(viewBuilder);
|
|
return viewBuilder;
|
|
};
|
|
|
|
/**
|
|
* Creates a new index bufferView in this buffer.
|
|
* @param {string} name The name of the bufferView.
|
|
* @returns {GltfBufferViewBuilder}
|
|
*/
|
|
GltfBufferBuilder.prototype.indexBuffer = function (name) {
|
|
var index =
|
|
this.gltfBuilder.gltf.bufferViews.push({
|
|
name: name,
|
|
buffer: this.bufferIndex,
|
|
byteLength: 0,
|
|
target: 34963,
|
|
}) - 1;
|
|
var viewBuilder = new GltfBufferViewBuilder(this, index, 5123);
|
|
this.viewBuilders.push(viewBuilder);
|
|
return viewBuilder;
|
|
};
|
|
|
|
/**
|
|
* A fluent interface for building a glTF bufferView.
|
|
* @param {GltfBufferBuilder} bufferBuilder The buffer builder.
|
|
* @param {GltfBufferViewBuilder} bufferViewIndex The bufferView builder.
|
|
* @param {number} componentType The glTF `componentType` of this bufferView.
|
|
* @private
|
|
*/
|
|
function GltfBufferViewBuilder(bufferBuilder, bufferViewIndex, componentType) {
|
|
this.bufferBuilder = bufferBuilder;
|
|
this.bufferViewIndex = bufferViewIndex;
|
|
this.bufferView = this.bufferBuilder.gltfBuilder.gltf.bufferViews[
|
|
this.bufferViewIndex
|
|
];
|
|
this.componentType = componentType;
|
|
this.elementStride = 0;
|
|
this.nextOffset = 0;
|
|
this._data = undefined;
|
|
}
|
|
|
|
Object.defineProperties(GltfBufferViewBuilder.prototype, {
|
|
/**
|
|
* Gets the number of bytes in each numeric element of this bufferView.
|
|
*/
|
|
elementByteLength: {
|
|
get: function () {
|
|
return this.componentType === 5126 ? 4 : 2;
|
|
},
|
|
},
|
|
});
|
|
|
|
/**
|
|
* Defines a `VEC3` element in this bufferView.
|
|
* @param {string} name The name of the accessor for this element.
|
|
* @returns {GltfBufferViewBuilder}
|
|
*/
|
|
GltfBufferViewBuilder.prototype.vec3 = function (name) {
|
|
var gltf = this.bufferBuilder.gltfBuilder.gltf;
|
|
gltf.accessors.push({
|
|
name: name,
|
|
bufferView: this.bufferViewIndex,
|
|
byteOffset: this.nextOffset,
|
|
componentType: this.componentType,
|
|
count: 0,
|
|
type: "VEC3",
|
|
});
|
|
|
|
this.elementStride += 3;
|
|
|
|
var newStride = 3 * this.elementByteLength;
|
|
if (!defined(this.bufferView.byteStride)) {
|
|
this.bufferView.byteStride = newStride;
|
|
} else {
|
|
this.bufferView.byteStride += newStride;
|
|
}
|
|
|
|
this.nextOffset += newStride;
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Defines a `SCALAR` element in this bufferView.
|
|
* @param {string} name The name of the accessor for this element.
|
|
* @returns {GltfBufferViewBuilder}
|
|
*/
|
|
GltfBufferViewBuilder.prototype.scalar = function (name) {
|
|
var gltf = this.bufferBuilder.gltfBuilder.gltf;
|
|
gltf.accessors.push({
|
|
name: name,
|
|
bufferView: this.bufferViewIndex,
|
|
byteOffset: this.nextOffset,
|
|
componentType: this.componentType,
|
|
count: 0,
|
|
type: "SCALAR",
|
|
});
|
|
|
|
this.elementStride += 1;
|
|
|
|
var newStride = this.elementByteLength;
|
|
if (!defined(this.bufferView.byteStride)) {
|
|
this.bufferView.byteStride = newStride;
|
|
} else {
|
|
this.bufferView.byteStride += newStride;
|
|
}
|
|
|
|
this.nextOffset += newStride;
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Provides this bufferView's data. All elements should be defined with
|
|
* {@link GltfBufferViewBuilder#vec3} and {@link GltfBufferViewBuilder#scalar}
|
|
* before calling this method.
|
|
* @param {Array|Float32Array|Uint16Array|Uint32Array} data The data.
|
|
* @returns {GltfBufferViewBuilder}
|
|
*/
|
|
GltfBufferViewBuilder.prototype.data = function (data) {
|
|
this.bufferView.byteLength = data.length * this.elementByteLength;
|
|
this._data = data;
|
|
|
|
var count = data.length / this.elementStride;
|
|
|
|
var gltf = this.bufferBuilder.gltfBuilder.gltf;
|
|
var bufferViewIndex = this.bufferViewIndex;
|
|
|
|
gltf.accessors
|
|
.filter(function (accessor) {
|
|
return accessor.bufferView === bufferViewIndex;
|
|
})
|
|
.forEach(function (accessor) {
|
|
accessor.count = count;
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
function findAccessorByName(gltf, accessorName) {
|
|
var accessors = gltf.accessors;
|
|
|
|
for (var i = 0; i < accessors.length; ++i) {
|
|
if (accessors[i].name === accessorName) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
export default GltfBuilder;
|