ModelHandle/source/import/importergltf.js

1086 lines
37 KiB
JavaScript

OV.GltfComponentType =
{
BYTE : 5120,
UNSIGNED_BYTE : 5121,
SHORT : 5122,
UNSIGNED_SHORT : 5123,
UNSIGNED_INT : 5125,
FLOAT : 5126
};
OV.GltfDataType =
{
SCALAR : 0,
VEC2 : 1,
VEC3 : 2,
VEC4 : 3,
MAT2 : 4,
MAT3 : 5,
MAT4 : 6
};
OV.GltfRenderMode =
{
POINTS : 0,
LINES : 1,
LINE_LOOP : 2,
LINE_STRIP : 3,
TRIANGLES : 4,
TRIANGLE_STRIP : 5,
TRIANGLE_FAN : 6
};
OV.GltfConstants =
{
GLTF_STRING : 0x46546C67,
JSON_CHUNK_TYPE : 0x4E4F534A,
BINARY_CHUNK_TYPE : 0x004E4942
};
OV.GltfNodeTree = class
{
constructor ()
{
this.nodes = [];
this.nodeToParent = {};
this.nodeMatrices = {};
}
AddMeshNode (nodeIndex)
{
this.nodes.push (nodeIndex);
return this.nodes.length - 1;
}
AddNodeParent (nodeIndex, parentIndex)
{
this.nodeToParent[nodeIndex] = parentIndex;
}
GetNodeParent (nodeIndex)
{
let parentIndex = this.nodeToParent[nodeIndex];
if (parentIndex === undefined || parentIndex === -1) {
return null;
}
return parentIndex;
}
AddNodeMatrix (nodeIndex, matrix)
{
this.nodeMatrices[nodeIndex] = matrix;
}
GetNodeMatrix (nodeIndex)
{
let matrix = this.nodeMatrices[nodeIndex];
if (matrix === undefined) {
return null;
}
return matrix;
}
};
OV.GltfBufferReader = class
{
constructor (buffer)
{
this.reader = new OV.BinaryReader (buffer, true);
this.componentType = null;
this.dataType = null;
this.byteStride = null;
this.dataCount = null;
this.sparseReader = null;
}
SetComponentType (componentType)
{
this.componentType = componentType;
}
SetDataType (dataType)
{
if (dataType === 'SCALAR') {
this.dataType = OV.GltfDataType.SCALAR;
} else if (dataType === 'VEC2') {
this.dataType = OV.GltfDataType.VEC2;
} else if (dataType === 'VEC3') {
this.dataType = OV.GltfDataType.VEC3;
} else if (dataType === 'VEC4') {
this.dataType = OV.GltfDataType.VEC4;
} else if (dataType === 'MAT2') {
this.dataType = OV.GltfDataType.MAT2;
} else if (dataType === 'MAT3') {
this.dataType = OV.GltfDataType.MAT3;
} else if (dataType === 'MAT4') {
this.dataType = OV.GltfDataType.MAT4;
}
}
SetByteStride (byteStride)
{
this.byteStride = byteStride;
}
SetDataCount (dataCount)
{
this.dataCount = dataCount;
}
SetSparseReader (indexReader, valueReader)
{
this.sparseReader = {
indexReader : indexReader,
valueReader : valueReader
};
}
ReadArrayBuffer (byteLength)
{
return this.reader.ReadArrayBuffer (byteLength);
}
GetDataCount ()
{
return this.dataCount;
}
ReadData ()
{
if (this.dataType === null) {
return null;
}
if (this.dataType === OV.GltfDataType.SCALAR) {
let data = this.ReadComponent ();
this.SkipBytesByStride (1);
return data;
} else if (this.dataType === OV.GltfDataType.VEC2) {
let x = this.ReadComponent ();
let y = this.ReadComponent ();
this.SkipBytesByStride (2);
return new OV.Coord2D (x, y);
} else if (this.dataType === OV.GltfDataType.VEC3) {
let x = this.ReadComponent ();
let y = this.ReadComponent ();
let z = this.ReadComponent ();
this.SkipBytesByStride (3);
return new OV.Coord3D (x, y, z);
}
return null;
}
EnumerateData (onData)
{
if (this.sparseReader === null) {
for (let i = 0; i < this.dataCount; i++) {
onData (this.ReadData ());
}
} else {
let sparseData = [];
for (let i = 0; i < this.sparseReader.indexReader.GetDataCount (); i++) {
let index = this.sparseReader.indexReader.ReadData ();
let value = this.sparseReader.valueReader.ReadData ();
sparseData.push ({
index : index,
value : value
});
}
let sparseIndex = 0;
for (let i = 0; i < this.dataCount; i++) {
let data = this.ReadData ();
if (sparseIndex < sparseData.length && sparseData[sparseIndex].index === i) {
onData (sparseData[sparseIndex].value);
sparseIndex += 1;
} else {
onData (data);
}
}
}
}
SkipBytes (bytes)
{
this.reader.Skip (bytes);
}
ReadComponent ()
{
if (this.componentType === null) {
return null;
}
if (this.componentType === OV.GltfComponentType.BYTE) {
return this.reader.ReadCharacter8 ();
} else if (this.componentType === OV.GltfComponentType.UNSIGNED_BYTE) {
return this.reader.ReadUnsignedCharacter8 ();
} else if (this.componentType === OV.GltfComponentType.SHORT) {
return this.reader.ReadInteger16 ();
} else if (this.componentType === OV.GltfComponentType.UNSIGNED_SHORT) {
return this.reader.ReadUnsignedInteger16 ();
} else if (this.componentType === OV.GltfComponentType.UNSIGNED_INT) {
return this.reader.ReadInteger32 ();
} else if (this.componentType === OV.GltfComponentType.FLOAT) {
return this.reader.ReadFloat32 ();
}
return null;
}
SkipBytesByStride (componentCount)
{
if (this.byteStride === null) {
return;
}
let readBytes = componentCount * this.GetComponentSize ();
this.reader.Skip (this.byteStride - readBytes);
}
GetComponentSize ()
{
if (this.componentType === OV.GltfComponentType.BYTE) {
return 1;
} else if (this.componentType === OV.GltfComponentType.UNSIGNED_BYTE) {
return 1;
} else if (this.componentType === OV.GltfComponentType.SHORT) {
return 2;
} else if (this.componentType === OV.GltfComponentType.UNSIGNED_SHORT) {
return 2;
} else if (this.componentType === OV.GltfComponentType.UNSIGNED_INT) {
return 4;
} else if (this.componentType === OV.GltfComponentType.FLOAT) {
return 4;
}
return 0;
}
};
OV.GltfExtensions = class
{
constructor ()
{
this.supportedExtensions = [
'KHR_draco_mesh_compression',
'KHR_materials_pbrSpecularGlossiness',
'KHR_texture_transform',
];
this.draco = null;
}
LoadLibraries (extensionsRequired, callbacks)
{
if (extensionsRequired === undefined) {
callbacks.onSuccess ();
return;
}
if (this.draco === null && extensionsRequired.indexOf ('KHR_draco_mesh_compression') !== -1) {
OV.LoadExternalLibrary ('loaders/draco_decoder.js').then (() => {
DracoDecoderModule ().then ((draco) => {
this.draco = draco;
callbacks.onSuccess ();
});
}).catch (() => {
callbacks.onError ();
});
} else {
callbacks.onSuccess ();
}
}
GetUnsupportedExtensions (extensionsRequired)
{
let unsupportedExtensions = [];
if (extensionsRequired === undefined) {
return unsupportedExtensions;
}
for (let i = 0; i < extensionsRequired.length; i++) {
let requiredExtension = extensionsRequired[i];
if (this.supportedExtensions.indexOf (requiredExtension) === -1) {
unsupportedExtensions.push (requiredExtension);
}
}
return unsupportedExtensions;
}
ProcessMaterial (gltfMaterial, material, imporTextureFn)
{
function GetMaterialComponent (component)
{
return parseInt (Math.round (OV.LinearToSRGB (component) * 255.0), 10);
}
if (gltfMaterial.extensions === undefined) {
return;
}
let khrSpecularGlossiness = gltfMaterial.extensions.KHR_materials_pbrSpecularGlossiness;
if (khrSpecularGlossiness !== undefined) {
material.type = OV.MaterialType.Phong;
let diffuseColor = khrSpecularGlossiness.diffuseFactor;
if (diffuseColor !== undefined) {
material.color = new OV.Color (
GetMaterialComponent (diffuseColor[0]),
GetMaterialComponent (diffuseColor[1]),
GetMaterialComponent (diffuseColor[2])
);
material.opacity = diffuseColor[3];
}
let diffuseTexture = khrSpecularGlossiness.diffuseTexture;
if (diffuseTexture !== undefined) {
material.diffuseMap = imporTextureFn (diffuseTexture);
}
let specularColor = khrSpecularGlossiness.specularFactor;
if (specularColor !== undefined) {
material.specular = new OV.Color (
GetMaterialComponent (specularColor[0]),
GetMaterialComponent (specularColor[1]),
GetMaterialComponent (specularColor[2])
);
}
let specularTexture = khrSpecularGlossiness.specularGlossinessTexture;
if (specularTexture !== undefined) {
material.specularMap = imporTextureFn (specularTexture);
}
let glossiness = khrSpecularGlossiness.glossinessFactor;
if (glossiness !== undefined) {
material.shininess = glossiness;
}
}
}
ProcessTexture (gltfTexture, texture)
{
if (gltfTexture.extensions === undefined) {
return;
}
let khrTextureTransform = gltfTexture.extensions.KHR_texture_transform;
if (khrTextureTransform !== undefined) {
if (khrTextureTransform.offset !== undefined) {
texture.offset.x = khrTextureTransform.offset[0];
texture.offset.y = -khrTextureTransform.offset[1];
}
if (khrTextureTransform.scale !== undefined) {
texture.scale.x = khrTextureTransform.scale[0];
texture.scale.y = khrTextureTransform.scale[1];
}
if (khrTextureTransform.rotation !== undefined) {
texture.rotation = -khrTextureTransform.rotation;
}
}
}
ProcessPrimitive (importer, gltf, primitive, mesh)
{
function EnumerateComponents (draco, decoder, dracoMesh, attributeId, processor)
{
let attribute = decoder.GetAttributeByUniqueId (dracoMesh, attributeId);
let numComponents = attribute.num_components ();
let numPoints = dracoMesh.num_points ();
let numValues = numPoints * numComponents;
let dataSize = numValues * 4;
let attributePtr = draco._malloc (dataSize);
decoder.GetAttributeDataArrayForAllPoints (dracoMesh, attribute, draco.DT_FLOAT32, dataSize, attributePtr);
let attributeArray = new Float32Array (draco.HEAPF32.buffer, attributePtr, numValues).slice ();
if (numComponents === 2) {
for (let i = 0; i < attributeArray.length; i += 2) {
processor (new OV.Coord2D (
attributeArray[i + 0],
attributeArray[i + 1]
));
}
} else if (numComponents === 3) {
for (let i = 0; i < attributeArray.length; i += 3) {
processor (new OV.Coord3D (
attributeArray[i + 0],
attributeArray[i + 1],
attributeArray[i + 2]
));
}
}
draco._free (attributePtr);
}
if (this.draco === null) {
return false;
}
if (primitive.extensions === undefined || primitive.extensions.KHR_draco_mesh_compression === undefined) {
return false;
}
let decoder = new this.draco.Decoder ();
let decoderBuffer = new this.draco.DecoderBuffer ();
let extensionParams = primitive.extensions.KHR_draco_mesh_compression;
let compressedBufferView = gltf.bufferViews[extensionParams.bufferView];
let compressedReader = importer.GetReaderFromBufferView (compressedBufferView);
let compressedArrayBuffer = compressedReader.ReadArrayBuffer (compressedBufferView.byteLength);
decoderBuffer.Init (new Int8Array (compressedArrayBuffer), compressedArrayBuffer.byteLength);
let geometryType = decoder.GetEncodedGeometryType (decoderBuffer);
if (geometryType !== this.draco.TRIANGULAR_MESH) {
return true;
}
let dracoMesh = new this.draco.Mesh ();
let decodingStatus = decoder.DecodeBufferToMesh (decoderBuffer, dracoMesh);
if (!decodingStatus.ok ()) {
return true;
}
let hasVertices = (extensionParams.attributes.POSITION !== undefined);
let hasNormals = (extensionParams.attributes.NORMAL !== undefined);
let hasUVs = (extensionParams.attributes.TEXCOORD_0 !== undefined);
if (!hasVertices) {
return true;
}
let vertexOffset = mesh.VertexCount ();
let normalOffset = mesh.NormalCount ();
let uvOffset = mesh.TextureUVCount ();
EnumerateComponents (this.draco, decoder, dracoMesh, extensionParams.attributes.POSITION, (vertex) => {
mesh.AddVertex (vertex);
});
if (hasNormals) {
EnumerateComponents (this.draco, decoder, dracoMesh, extensionParams.attributes.NORMAL, (normal) => {
mesh.AddNormal (normal);
});
}
if (hasUVs) {
EnumerateComponents (this.draco, decoder, dracoMesh, extensionParams.attributes.TEXCOORD_0, (uv) => {
uv.y = -uv.y;
mesh.AddTextureUV (uv);
});
}
let faceCount = dracoMesh.num_faces ();
let indexCount = faceCount * 3;
let indexDataSize = indexCount * 4;
let indexDataPtr = this.draco._malloc (indexDataSize);
decoder.GetTrianglesUInt32Array (dracoMesh, indexDataSize, indexDataPtr);
let indexArray = new Uint32Array (this.draco.HEAPU32.buffer, indexDataPtr, indexCount).slice ();
for (let i = 0; i < indexArray.length; i += 3) {
let v0 = indexArray[i];
let v1 = indexArray[i + 1];
let v2 = indexArray[i + 2];
importer.AddTriangle (primitive, mesh, v0, v1, v2, hasNormals, hasUVs, vertexOffset, normalOffset, uvOffset);
}
this.draco._free (indexDataPtr);
return true;
}
};
OV.ImporterGltf = class extends OV.ImporterBase
{
constructor ()
{
super ();
this.gltfExtensions = new OV.GltfExtensions ();
}
CanImportExtension (extension)
{
return extension === 'gltf' || extension === 'glb';
}
GetUpDirection ()
{
return OV.Direction.Y;
}
ClearContent ()
{
this.bufferContents = null;
this.imageIndexToTextureParams = null;
}
ResetContent ()
{
this.bufferContents = [];
this.imageIndexToTextureParams = {};
}
ImportContent (fileContent, onFinish)
{
if (this.extension === 'gltf') {
this.ProcessGltf (fileContent, onFinish);
} else if (this.extension === 'glb') {
this.ProcessBinaryGltf (fileContent, onFinish);
}
}
ProcessGltf (fileContent, onFinish)
{
let textContent = OV.ArrayBufferToUtf8String (fileContent);
let gltf = JSON.parse (textContent);
if (gltf.asset.version !== '2.0') {
this.SetError ('Invalid glTF version.');
onFinish ();
return;
}
for (let i = 0; i < gltf.buffers.length; i++) {
let buffer = null;
let gltfBuffer = gltf.buffers[i];
let base64Buffer = OV.Base64DataURIToArrayBuffer (gltfBuffer.uri);
if (base64Buffer !== null) {
buffer = base64Buffer.buffer;
} else {
let fileBuffer = this.callbacks.getFileBuffer (gltfBuffer.uri);
if (fileBuffer !== null) {
buffer = fileBuffer;
}
}
if (buffer === null) {
this.SetError ('One of the requested buffers is missing.');
onFinish ();
return;
}
this.bufferContents.push (buffer);
}
this.ProcessMainFile (gltf, onFinish);
}
ProcessBinaryGltf (fileContent, onFinish)
{
function ReadChunk (reader)
{
let length = reader.ReadUnsignedInteger32 ();
let type = reader.ReadUnsignedInteger32 ();
let buffer = reader.ReadArrayBuffer (length);
return {
type : type,
buffer : buffer
};
}
let reader = new OV.BinaryReader (fileContent, true);
let magic = reader.ReadUnsignedInteger32 ();
if (magic !== OV.GltfConstants.GLTF_STRING) {
this.SetError ('Invalid glTF file.');
onFinish ();
return;
}
let version = reader.ReadUnsignedInteger32 ();
if (version !== 2) {
this.SetError ('Invalid glTF version.');
onFinish ();
return;
}
let length = reader.ReadUnsignedInteger32 ();
if (length !== reader.GetByteLength ()) {
this.SetError ('Invalid glTF file.');
onFinish ();
return;
}
let gltfTextContent = null;
while (!reader.End ()) {
let chunk = ReadChunk (reader);
if (chunk.type === OV.GltfConstants.JSON_CHUNK_TYPE) {
gltfTextContent = OV.ArrayBufferToUtf8String (chunk.buffer);
} else if (chunk.type === OV.GltfConstants.BINARY_CHUNK_TYPE) {
this.bufferContents.push (chunk.buffer);
}
}
if (gltfTextContent !== null) {
let gltf = JSON.parse (gltfTextContent);
this.ProcessMainFile (gltf, onFinish);
}
}
ProcessMainFile (gltf, onFinish)
{
let unsupportedExtensions = this.gltfExtensions.GetUnsupportedExtensions (gltf.extensionsRequired);
if (unsupportedExtensions.length > 0) {
this.SetError ('Unsupported extension: ' + unsupportedExtensions.join (', ') + '.');
onFinish ();
return;
}
this.gltfExtensions.LoadLibraries (gltf.extensionsRequired, {
onSuccess : () => {
this.ImportModel (gltf);
onFinish ();
},
onError : () => {
this.SetError ('Failed to load draco decoder.');
onFinish ();
}
});
}
ImportModel (gltf)
{
let defaultScene = this.GetDefaultScene (gltf);
if (defaultScene === null) {
this.SetError ('No default scene found.');
return;
}
this.ImportModelProperties (gltf);
let materials = gltf.materials;
if (materials !== undefined) {
for (let i = 0; i < materials.length; i++) {
this.ImportMaterial (gltf, i);
}
}
let nodeTree = this.CollectMeshNodesForScene (gltf, defaultScene);
for (let i = 0; i < nodeTree.nodes.length; i++) {
let nodeIndex = nodeTree.nodes[i];
this.ImportMeshNode (gltf, nodeIndex, nodeTree);
}
}
ImportModelProperties (gltf)
{
function ImportProperties (model, propertyGroupName, propertyObject)
{
let propertyGroup = new OV.PropertyGroup (propertyGroupName);
for (let propertyName in propertyObject) {
if (Object.prototype.hasOwnProperty.call (propertyObject, propertyName)) {
if (typeof propertyObject[propertyName] === 'string') {
const property = new OV.Property (OV.PropertyType.Text, propertyName, propertyObject[propertyName]);
propertyGroup.AddProperty (property);
}
}
}
if (propertyGroup.PropertyCount () > 0) {
model.AddPropertyGroup (propertyGroup);
}
return propertyGroup;
}
ImportProperties (this.model, 'Asset properties', gltf.asset);
if (gltf.asset['extras']) {
ImportProperties (this.model, 'Extras', gltf.asset['extras']);
}
}
GetDefaultScene (gltf)
{
let defaultSceneIndex = gltf.scene || 0;
if (defaultSceneIndex >= gltf.scenes.length) {
return null;
}
return gltf.scenes[defaultSceneIndex];
}
CollectMeshNodesForScene (gltf, scene)
{
function CollectMeshNodeIndices (gltf, parentIndex, nodeIndex, nodeTree)
{
let node = gltf.nodes[nodeIndex];
if (node.mesh !== undefined) {
nodeTree.AddMeshNode (nodeIndex);
}
nodeTree.AddNodeParent (nodeIndex, parentIndex);
if (node.children !== undefined) {
for (let i = 0; i < node.children.length; i++) {
let childNodeIndex = node.children[i];
CollectMeshNodeIndices (gltf, nodeIndex, childNodeIndex, nodeTree);
}
}
}
let nodeTree = new OV.GltfNodeTree ();
for (let i = 0; i < scene.nodes.length; i++) {
let nodeIndex = scene.nodes[i];
CollectMeshNodeIndices (gltf, -1, nodeIndex, nodeTree);
}
return nodeTree;
}
ImportMaterial (gltf, materialIndex)
{
function GetMaterialComponent (component)
{
return parseInt (Math.round (OV.LinearToSRGB (component) * 255.0), 10);
}
let gltfMaterial = gltf.materials[materialIndex];
let material = new OV.Material (OV.MaterialType.Physical);
if (gltfMaterial.name !== undefined) {
material.name = gltfMaterial.name;
}
material.color = new OV.Color (
GetMaterialComponent (1.0),
GetMaterialComponent (1.0),
GetMaterialComponent (1.0)
);
if (gltfMaterial.pbrMetallicRoughness !== undefined) {
let baseColor = gltfMaterial.pbrMetallicRoughness.baseColorFactor;
if (baseColor !== undefined) {
material.color = new OV.Color (
GetMaterialComponent (baseColor[0]),
GetMaterialComponent (baseColor[1]),
GetMaterialComponent (baseColor[2])
);
material.opacity = baseColor[3];
}
let metallicFactor = gltfMaterial.pbrMetallicRoughness.metallicFactor;
if (metallicFactor !== undefined) {
material.metalness = metallicFactor;
}
let roughnessFactor = gltfMaterial.pbrMetallicRoughness.roughnessFactor;
if (roughnessFactor !== undefined) {
material.roughness = roughnessFactor;
}
let emissiveColor = gltfMaterial.emissiveFactor;
if (emissiveColor !== undefined) {
material.emissive = new OV.Color (
GetMaterialComponent (emissiveColor[0]),
GetMaterialComponent (emissiveColor[1]),
GetMaterialComponent (emissiveColor[2])
);
}
material.diffuseMap = this.ImportTexture (gltf, gltfMaterial.pbrMetallicRoughness.baseColorTexture);
material.metalnessMap = this.ImportTexture (gltf, gltfMaterial.pbrMetallicRoughness.metallicRoughnessTexture);
material.normalMap = this.ImportTexture (gltf, gltfMaterial.normalTexture);
material.emissiveMap = this.ImportTexture (gltf, gltfMaterial.emissiveTexture);
if (material.diffuseMap !== null) {
material.multiplyDiffuseMap = true;
}
let alphaMode = gltfMaterial.alphaMode;
if (alphaMode !== undefined) {
if (alphaMode === 'BLEND') {
material.transparent = true;
} else if (alphaMode === 'MASK') {
material.transparent = true;
material.alphaTest = gltfMaterial.alphaCutoff || 0.5;
}
}
}
this.gltfExtensions.ProcessMaterial (gltfMaterial, material, (textureRef) => {
return this.ImportTexture (gltf, textureRef);
});
this.model.AddMaterial (material);
}
ImportTexture (gltf, gltfTextureRef)
{
if (gltfTextureRef === undefined || gltfTextureRef === null) {
return null;
}
let texture = new OV.TextureMap ();
let gltfTexture = gltf.textures[gltfTextureRef.index];
let gltfImageIndex = gltfTexture.source;
let gltfImage = gltf.images[gltfImageIndex];
let textureParams = this.imageIndexToTextureParams[gltfImageIndex];
if (textureParams === undefined) {
textureParams = {
name : null,
url : null,
buffer : null
};
let textureIndexString = gltfImageIndex.toString ();
if (gltfImage.uri !== undefined) {
let base64Buffer = OV.Base64DataURIToArrayBuffer (gltfImage.uri);
if (base64Buffer !== null) {
textureParams.name = 'Embedded_' + textureIndexString + '.' + OV.GetFileExtensionFromMimeType (base64Buffer.mimeType);
textureParams.url = OV.CreateObjectUrlWithMimeType (base64Buffer.buffer, base64Buffer.mimeType);
textureParams.buffer = base64Buffer.buffer;
} else {
let textureBuffer = this.callbacks.getTextureBuffer (gltfImage.uri);
textureParams.name = gltfImage.uri;
if (textureBuffer !== null) {
textureParams.url = textureBuffer.url;
textureParams.buffer = textureBuffer.buffer;
}
}
} else if (gltfImage.bufferView !== undefined) {
let bufferView = gltf.bufferViews[gltfImage.bufferView];
let reader = this.GetReaderFromBufferView (bufferView);
if (reader !== null) {
let buffer = reader.ReadArrayBuffer (bufferView.byteLength);
textureParams.name = 'Binary_' + textureIndexString + '.' + OV.GetFileExtensionFromMimeType (gltfImage.mimeType);
textureParams.url = OV.CreateObjectUrlWithMimeType (buffer, gltfImage.mimeType);
textureParams.buffer = buffer;
}
}
this.imageIndexToTextureParams[gltfImageIndex] = textureParams;
}
texture.name = textureParams.name;
texture.url = textureParams.url;
texture.buffer = textureParams.buffer;
this.gltfExtensions.ProcessTexture (gltfTextureRef, texture);
return texture;
}
ImportMeshNode (gltf, nodeIndex, nodeTree)
{
function GetNodeTransformation (gltf, nodeIndex, nodeTree)
{
let node = gltf.nodes[nodeIndex];
let matrix = nodeTree.GetNodeMatrix (nodeIndex);
if (matrix !== null) {
return matrix;
}
matrix = new OV.Matrix ().CreateIdentity ();
if (node.matrix !== undefined) {
matrix.Set (node.matrix);
} else {
let hasTransformation = false;
let translation = [0.0, 0.0, 0.0];
let rotation = [0.0, 0.0, 0.0, 1.0];
let scale = [1.0, 1.0, 1.0];
if (node.translation !== undefined) {
translation = node.translation;
hasTransformation = true;
}
if (node.rotation !== undefined) {
rotation = node.rotation;
hasTransformation = true;
}
if (node.scale !== undefined) {
scale = node.scale;
hasTransformation = true;
}
if (hasTransformation) {
matrix.ComposeTRS (
OV.ArrayToCoord3D (translation),
OV.ArrayToQuaternion (rotation),
OV.ArrayToCoord3D (scale)
);
}
}
let parentNodeIndex = nodeTree.GetNodeParent (nodeIndex);
if (parentNodeIndex !== null) {
let parentMatrix = GetNodeTransformation (gltf, parentNodeIndex, nodeTree);
matrix = matrix.MultiplyMatrix (parentMatrix);
}
nodeTree.AddNodeMatrix (nodeIndex, matrix);
return matrix;
}
let gltfNode = gltf.nodes[nodeIndex];
let gltfMeshIndex = gltfNode.mesh;
let gltfMesh = gltf.meshes[gltfMeshIndex];
let mesh = new OV.Mesh ();
this.model.AddMeshToRootNode (mesh);
if (gltfMesh.name !== undefined) {
mesh.SetName (gltfMesh.name);
} else if (gltfNode.name !== undefined) {
mesh.SetName (gltfNode.name);
}
for (let i = 0; i < gltfMesh.primitives.length; i++) {
let primitive = gltfMesh.primitives[i];
this.ImportPrimitive (gltf, primitive, mesh);
}
let matrix = GetNodeTransformation (gltf, nodeIndex, nodeTree);
let transformation = new OV.Transformation (matrix);
OV.TransformMesh (mesh, transformation);
}
ImportPrimitive (gltf, primitive, mesh)
{
if (this.gltfExtensions.ProcessPrimitive (this, gltf, primitive, mesh)) {
return;
}
if (primitive.attributes === undefined) {
return;
}
let hasVertices = (primitive.attributes.POSITION !== undefined);
let hasNormals = (primitive.attributes.NORMAL !== undefined);
let hasUVs = (primitive.attributes.TEXCOORD_0 !== undefined);
let hasIndices = (primitive.indices !== undefined);
let mode = OV.GltfRenderMode.TRIANGLES;
if (primitive.mode !== undefined) {
mode = primitive.mode;
}
if (mode !== OV.GltfRenderMode.TRIANGLES && mode !== OV.GltfRenderMode.TRIANGLE_STRIP && mode !== OV.GltfRenderMode.TRIANGLE_FAN) {
return;
}
let vertexOffset = mesh.VertexCount ();
let normalOffset = mesh.NormalCount ();
let uvOffset = mesh.TextureUVCount ();
if (hasVertices) {
let accessor = gltf.accessors[primitive.attributes.POSITION];
let reader = this.GetReaderFromAccessor (gltf, accessor);
if (reader === null) {
return;
}
reader.EnumerateData ((data) => {
mesh.AddVertex (data);
});
} else {
return;
}
if (hasNormals) {
let accessor = gltf.accessors[primitive.attributes.NORMAL];
let reader = this.GetReaderFromAccessor (gltf, accessor);
if (reader === null) {
return;
}
reader.EnumerateData ((data) => {
mesh.AddNormal (data);
});
}
if (hasUVs) {
let accessor = gltf.accessors[primitive.attributes.TEXCOORD_0];
let reader = this.GetReaderFromAccessor (gltf, accessor);
if (reader === null) {
return;
}
reader.EnumerateData ((data) => {
data.y = -data.y;
mesh.AddTextureUV (data);
});
}
let vertexIndices = [];
if (hasIndices) {
let accessor = gltf.accessors[primitive.indices];
let reader = this.GetReaderFromAccessor (gltf, accessor);
if (reader === null) {
return;
}
reader.EnumerateData ((data) => {
vertexIndices.push (data);
});
} else {
for (let i = 0; i < mesh.VertexCount (); i++) {
vertexIndices.push (i);
}
}
if (mode === OV.GltfRenderMode.TRIANGLES) {
for (let i = 0; i < vertexIndices.length; i += 3) {
let v0 = vertexIndices[i];
let v1 = vertexIndices[i + 1];
let v2 = vertexIndices[i + 2];
this.AddTriangle (primitive, mesh, v0, v1, v2, hasNormals, hasUVs, vertexOffset, normalOffset, uvOffset);
}
} else if (mode === OV.GltfRenderMode.TRIANGLE_STRIP) {
for (let i = 0; i < vertexIndices.length - 2; i++) {
let v0 = vertexIndices[i];
let v1 = vertexIndices[i + 1];
let v2 = vertexIndices[i + 2];
if (i % 2 === 1) {
let tmp = v1;
v1 = v2;
v2 = tmp;
}
this.AddTriangle (primitive, mesh, v0, v1, v2, hasNormals, hasUVs, vertexOffset, normalOffset, uvOffset);
}
} else if (mode === OV.GltfRenderMode.TRIANGLE_FAN) {
for (let i = 1; i < vertexIndices.length - 1; i++) {
let v0 = vertexIndices[0];
let v1 = vertexIndices[i];
let v2 = vertexIndices[i + 1];
this.AddTriangle (primitive, mesh, v0, v1, v2, hasNormals, hasUVs, vertexOffset, normalOffset, uvOffset);
}
}
}
AddTriangle (primitive, mesh, v0, v1, v2, hasNormals, hasUVs, vertexOffset, normalOffset, uvOffset)
{
let triangle = new OV.Triangle (vertexOffset + v0, vertexOffset + v1, vertexOffset + v2);
if (hasNormals) {
triangle.SetNormals (
normalOffset + v0,
normalOffset + v1,
normalOffset + v2
);
}
if (hasUVs) {
triangle.SetTextureUVs (
uvOffset + v0,
uvOffset + v1,
uvOffset + v2
);
}
if (primitive.material !== undefined) {
triangle.mat = primitive.material;
}
mesh.AddTriangle (triangle);
}
GetReaderFromBufferView (bufferView)
{
let bufferIndex = bufferView.buffer || 0;
let buffer = this.bufferContents[bufferIndex];
if (buffer === undefined || buffer === null) {
return null;
}
let reader = new OV.GltfBufferReader (buffer);
reader.SkipBytes (bufferView.byteOffset || 0);
let byteStride = bufferView.byteStride;
if (byteStride !== undefined && byteStride !== 0) {
reader.SetByteStride (byteStride);
}
return reader;
}
GetReaderFromAccessor (gltf, accessor)
{
let bufferViewIndex = accessor.bufferView || 0;
let bufferView = gltf.bufferViews[bufferViewIndex];
let reader = this.GetReaderFromBufferView (bufferView);
if (reader === null) {
return null;
}
reader.SetComponentType (accessor.componentType);
reader.SetDataType (accessor.type);
reader.SetDataCount (accessor.count);
reader.SkipBytes (accessor.byteOffset || 0);
if (accessor.sparse !== undefined) {
let indexReader = this.GetReaderFromSparseAccessor (gltf, accessor.sparse.indices, accessor.sparse.indices.componentType, 'SCALAR', accessor.sparse.count);
let valueReader = this.GetReaderFromSparseAccessor (gltf, accessor.sparse.values, accessor.componentType, accessor.type, accessor.sparse.count);
if (indexReader !== null && valueReader !== null) {
reader.SetSparseReader (indexReader, valueReader);
}
}
return reader;
}
GetReaderFromSparseAccessor (gltf, sparseAccessor, componentType, type, count)
{
if (sparseAccessor.bufferView === undefined) {
return null;
}
let bufferView = gltf.bufferViews[sparseAccessor.bufferView];
let reader = this.GetReaderFromBufferView (bufferView);
if (reader === null) {
return null;
}
reader.SetComponentType (componentType);
reader.SetDataType (type);
reader.SetDataCount (count);
reader.SkipBytes (sparseAccessor.byteOffset || 0);
return reader;
}
};