ModelHandle/source/import/importer3ds.js

683 lines
19 KiB
JavaScript

OV.CHUNK3DS =
{
MAIN3DS : 0x4D4D,
EDIT3DS : 0x3D3D,
EDIT_MATERIAL : 0xAFFF,
MAT_NAME : 0xA000,
MAT_AMBIENT : 0xA010,
MAT_DIFFUSE : 0xA020,
MAT_SPECULAR : 0xA030,
MAT_SHININESS : 0xA040,
MAT_SHININESS_STRENGTH : 0xA041,
MAT_TRANSPARENCY : 0xA050,
MAT_COLOR_F : 0x0010,
MAT_COLOR : 0x0011,
MAT_LIN_COLOR : 0x0012,
MAT_LIN_COLOR_F : 0x0013,
MAT_TEXMAP : 0xA200,
MAT_TEXMAP_NAME : 0xA300,
MAT_TEXMAP_UOFFSET : 0xA358,
MAT_TEXMAP_VOFFSET : 0xA35A,
MAT_TEXMAP_USCALE : 0xA354,
MAT_TEXMAP_VSCALE : 0xA356,
MAT_TEXMAP_ROTATION : 0xA35C,
PERCENTAGE : 0x0030,
PERCENTAGE_F : 0x0031,
EDIT_OBJECT : 0x4000,
OBJ_TRIMESH : 0x4100,
OBJ_LIGHT : 0x4600,
OBJ_CAMERA : 0x4700,
TRI_VERTEX : 0x4110,
TRI_TEXVERTEX : 0x4140,
TRI_FACE : 0x4120,
TRI_TRANSFORMATION : 0x4160,
TRI_MATERIAL : 0x4130,
TRI_SMOOTH : 0x4150,
KF3DS : 0xB000,
OBJECT_NODE : 0xB002,
OBJECT_HIERARCHY : 0xB010,
OBJECT_INSTANCE_NAME : 0xB011,
OBJECT_PIVOT : 0xB013,
OBJECT_POSITION : 0xB020,
OBJECT_ROTATION : 0xB021,
OBJECT_SCALE : 0xB022,
OBJECT_ID : 0xB030
};
OV.Importer3ds = class extends OV.ImporterBase
{
constructor ()
{
super ();
this.materialNameToIndex = null;
this.meshNameToIndex = null;
this.meshTransformations = null;
this.defaultMaterialIndex = null;
}
ResetState ()
{
this.materialNameToIndex = {};
this.meshNameToIndex = {};
this.meshTransformations = [];
this.defaultMaterialIndex = null;
}
CanImportExtension (extension)
{
return extension === '3ds';
}
GetKnownFileFormats ()
{
return {
'3ds' : OV.FileFormat.Binary
};
}
GetUpDirection ()
{
return OV.Direction.Z;
}
ImportContent (fileContent)
{
this.ProcessBinary (fileContent);
}
ProcessBinary (fileContent)
{
let obj = this;
let reader = new OV.BinaryReader (fileContent, true);
let endByte = reader.GetByteLength ();
this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
if (chunkId === OV.CHUNK3DS.MAIN3DS) {
obj.ReadMainChunk (reader, chunkLength);
} else {
obj.SkipChunk (reader, chunkLength);
}
});
}
ReadMainChunk (reader, length)
{
let obj = this;
let endByte = this.GetChunkEnd (reader, length);
this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
if (chunkId === OV.CHUNK3DS.EDIT3DS) {
obj.ReadEditorChunk (reader, chunkLength);
} else if (chunkId === OV.CHUNK3DS.KF3DS) {
obj.ReadKeyFrameChunk (reader, chunkLength);
} else {
obj.SkipChunk (reader, chunkLength);
}
});
}
ReadEditorChunk (reader, length)
{
let obj = this;
let endByte = this.GetChunkEnd (reader, length);
this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
if (chunkId === OV.CHUNK3DS.EDIT_MATERIAL) {
obj.ReadMaterialChunk (reader, chunkLength);
} else if (chunkId === OV.CHUNK3DS.EDIT_OBJECT) {
obj.ReadObjectChunk (reader, chunkLength);
} else {
obj.SkipChunk (reader, chunkLength);
}
});
}
ReadMaterialChunk (reader, length)
{
let obj = this;
let material = new OV.Material ();
let endByte = this.GetChunkEnd (reader, length);
let shininess = null;
let shininessStrength = null;
this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
if (chunkId === OV.CHUNK3DS.MAT_NAME) {
material.name = obj.ReadName (reader);
} else if (chunkId === OV.CHUNK3DS.MAT_AMBIENT) {
material.ambient = obj.ReadColorChunk (reader, chunkLength);
} else if (chunkId === OV.CHUNK3DS.MAT_DIFFUSE) {
material.diffuse = obj.ReadColorChunk (reader, chunkLength);
} else if (chunkId === OV.CHUNK3DS.MAT_SPECULAR) {
material.specular = obj.ReadColorChunk (reader, chunkLength);
} else if (chunkId === OV.CHUNK3DS.MAT_SHININESS) {
shininess = obj.ReadPercentageChunk (reader, chunkLength);
} else if (chunkId === OV.CHUNK3DS.MAT_SHININESS_STRENGTH) {
shininessStrength = obj.ReadPercentageChunk (reader, chunkLength);
} else if (chunkId === OV.CHUNK3DS.MAT_TRANSPARENCY) {
material.opacity = 1.0 - obj.ReadPercentageChunk (reader, chunkLength);
material.transparent = OV.IsLower (material.opacity, 1.0);
} else if (chunkId === OV.CHUNK3DS.MAT_TEXMAP) {
material.diffuseMap = obj.ReadTextureMapChunk (reader, chunkLength);
} else {
obj.SkipChunk (reader, chunkLength);
}
});
if (shininess !== null || shininessStrength !== null) {
material.shininess = shininess * shininessStrength;
}
let materialIndex = this.model.AddMaterial (material);
this.materialNameToIndex[material.name] = materialIndex;
}
ReadTextureMapChunk (reader, length)
{
let obj = this;
let texture = new OV.TextureMap ();
let endByte = this.GetChunkEnd (reader, length);
this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
if (chunkId === OV.CHUNK3DS.MAT_TEXMAP_NAME) {
let textureName = obj.ReadName (reader);
let textureBuffer = obj.GetTextureBuffer (textureName);
texture.name = textureName;
if (textureBuffer !== null) {
texture.url = textureBuffer.url;
texture.buffer = textureBuffer.buffer;
}
} else if (chunkId === OV.CHUNK3DS.MAT_TEXMAP_UOFFSET) {
texture.offset.x = reader.ReadFloat32 ();
} else if (chunkId === OV.CHUNK3DS.MAT_TEXMAP_VOFFSET) {
texture.offset.y = reader.ReadFloat32 ();
} else if (chunkId === OV.CHUNK3DS.MAT_TEXMAP_USCALE) {
texture.scale.x = reader.ReadFloat32 ();
} else if (chunkId === OV.CHUNK3DS.MAT_TEXMAP_VSCALE) {
texture.scale.y = reader.ReadFloat32 ();
} else if (chunkId === OV.CHUNK3DS.MAT_TEXMAP_ROTATION) {
texture.rotation = reader.ReadFloat32 () * OV.DegRad;
} else {
obj.SkipChunk (reader, chunkLength);
}
});
return texture;
}
ReadColorChunk (reader, length)
{
let obj = this;
let color = new OV.Color (0, 0, 0);
let endByte = this.GetChunkEnd (reader, length);
let hasLinColor = false;
this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
if (chunkId === OV.CHUNK3DS.MAT_COLOR) {
if (!hasLinColor) {
color.r = reader.ReadUnsignedCharacter8 ();
color.g = reader.ReadUnsignedCharacter8 ();
color.b = reader.ReadUnsignedCharacter8 ();
}
} else if (chunkId === OV.CHUNK3DS.MAT_LIN_COLOR) {
color.r = reader.ReadUnsignedCharacter8 ();
color.g = reader.ReadUnsignedCharacter8 ();
color.b = reader.ReadUnsignedCharacter8 ();
hasLinColor = true;
} else if (chunkId === OV.CHUNK3DS.MAT_COLOR_F) {
if (!hasLinColor) {
color.r = parseInt (reader.ReadFloat32 () * 255.0, 10);
color.g = parseInt (reader.ReadFloat32 () * 255.0, 10);
color.b = parseInt (reader.ReadFloat32 () * 255.0, 10);
}
} else if (chunkId === OV.CHUNK3DS.MAT_LIN_COLOR_F) {
color.r = parseInt (reader.ReadFloat32 () * 255.0, 10);
color.g = parseInt (reader.ReadFloat32 () * 255.0, 10);
color.b = parseInt (reader.ReadFloat32 () * 255.0, 10);
hasLinColor = true;
} else {
obj.SkipChunk (reader, chunkLength);
}
});
return color;
}
ReadPercentageChunk (reader, length)
{
let obj = this;
let percentage = 0.0;
let endByte = this.GetChunkEnd (reader, length);
this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
if (chunkId === OV.CHUNK3DS.PERCENTAGE) {
percentage = reader.ReadUnsignedInteger16 () / 100.0;
} else if (chunkId === OV.CHUNK3DS.PERCENTAGE_F) {
percentage = reader.ReadFloat32 ();
} else {
obj.SkipChunk (reader, chunkLength);
}
});
return percentage;
}
ReadObjectChunk (reader, length)
{
let obj = this;
let endByte = this.GetChunkEnd (reader, length);
let objectName = this.ReadName (reader);
this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
if (chunkId === OV.CHUNK3DS.OBJ_TRIMESH) {
obj.ReadMeshChunk (reader, chunkLength, objectName);
} else if (chunkId === OV.CHUNK3DS.OBJ_LIGHT) {
obj.SkipChunk (reader, chunkLength);
} else if (chunkId === OV.CHUNK3DS.OBJ_CAMERA) {
obj.SkipChunk (reader, chunkLength);
} else {
obj.SkipChunk (reader, chunkLength);
}
});
}
ReadMeshChunk (reader, length, objectName)
{
let obj = this;
let mesh = new OV.Mesh ();
mesh.SetName (objectName);
let endByte = this.GetChunkEnd (reader, length);
let transformation = null;
this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
if (chunkId === OV.CHUNK3DS.TRI_VERTEX) {
obj.ReadVerticesChunk (mesh, reader);
} else if (chunkId === OV.CHUNK3DS.TRI_TEXVERTEX) {
obj.ReadTextureVerticesChunk (mesh, reader);
} else if (chunkId === OV.CHUNK3DS.TRI_FACE) {
obj.ReadFacesChunk (mesh, reader, chunkLength);
} else if (chunkId === OV.CHUNK3DS.TRI_TRANSFORMATION) {
transformation = obj.ReadTransformationChunk (reader);
} else {
obj.SkipChunk (reader, chunkLength);
}
});
if (mesh.VertexCount () === mesh.TextureUVCount ()) {
for (let i = 0; i < mesh.TriangleCount (); i++) {
let triangle = mesh.GetTriangle (i);
triangle.SetTextureUVs (
triangle.v0,
triangle.v1,
triangle.v2
);
}
}
let meshIndex = this.model.AddMesh (mesh);
this.meshNameToIndex[mesh.GetName ()] = meshIndex;
this.meshTransformations.push (new OV.Matrix (transformation));
}
ReadVerticesChunk (mesh, reader)
{
let vertexCount = reader.ReadUnsignedInteger16 ();
for (let i = 0; i < vertexCount; i++) {
let x = reader.ReadFloat32 ();
let y = reader.ReadFloat32 ();
let z = reader.ReadFloat32 ();
mesh.AddVertex (new OV.Coord3D (x, y, z));
}
}
ReadTextureVerticesChunk (mesh, reader)
{
let texVertexCount = reader.ReadUnsignedInteger16 ();
for (let i = 0; i < texVertexCount; i++) {
let x = reader.ReadFloat32 ();
let y = reader.ReadFloat32 ();
mesh.AddTextureUV (new OV.Coord2D (x, y));
}
}
ReadFacesChunk (mesh, reader, length)
{
let endByte = this.GetChunkEnd (reader, length);
let faceCount = reader.ReadUnsignedInteger16 ();
for (let i = 0; i < faceCount; i++) {
let v0 = reader.ReadUnsignedInteger16 ();
let v1 = reader.ReadUnsignedInteger16 ();
let v2 = reader.ReadUnsignedInteger16 ();
reader.ReadUnsignedInteger16 (); // flags
mesh.AddTriangle (new OV.Triangle (v0, v1, v2));
}
let obj = this;
this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
if (chunkId === OV.CHUNK3DS.TRI_MATERIAL) {
obj.ReadFaceMaterialsChunk (mesh, reader);
} else if (chunkId === OV.CHUNK3DS.TRI_SMOOTH) {
obj.ReadFaceSmoothingGroupsChunk (mesh, faceCount, reader);
} else {
obj.SkipChunk (reader, chunkLength);
}
});
}
ReadFaceMaterialsChunk (mesh, reader)
{
let materialName = this.ReadName (reader);
let materialIndex = this.materialNameToIndex[materialName];
let faceCount = reader.ReadUnsignedInteger16 ();
for (let i = 0; i < faceCount; i++) {
let faceIndex = reader.ReadUnsignedInteger16 ();
let triangle = mesh.GetTriangle (faceIndex);
if (materialIndex !== undefined) {
triangle.mat = materialIndex;
}
}
}
ReadFaceSmoothingGroupsChunk (mesh, faceCount, reader)
{
for (let i = 0; i < faceCount; i++) {
let smoothingGroup = reader.ReadUnsignedInteger32 ();
let triangle = mesh.GetTriangle (i);
triangle.curve = smoothingGroup;
}
}
ReadTransformationChunk (reader)
{
let matrix = [];
for (let i = 0; i < 4; i++) {
for (let j = 0; j < 3; j++) {
matrix.push (reader.ReadFloat32 ());
}
if (i < 3) {
matrix.push (0);
} else {
matrix.push (1);
}
}
return matrix;
}
ReadKeyFrameChunk (reader, length)
{
let nodeHierarchy = {
nodes : [],
idToIndex : {},
meshIndexToNodes : {}
};
let endByte = this.GetChunkEnd (reader, length);
let obj = this;
this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
if (chunkId === OV.CHUNK3DS.OBJECT_NODE) {
obj.ReadObjectNodeChunk (nodeHierarchy, reader, chunkLength);
} else {
obj.SkipChunk (reader, chunkLength);
}
});
this.ApplyModelTransformations (nodeHierarchy);
}
ApplyModelTransformations (nodeHierarchy)
{
function GetNodeTransformation (nodeHierarchy, node)
{
function GetNodePosition (node)
{
if (node.positions.length === 0) {
return [0.0, 0.0, 0.0];
}
return node.positions[0];
}
function GetNodeRotation (node)
{
function GetQuaternionFromAxisAndAngle (rotation)
{
let result = [0.0, 0.0, 0.0, 1.0];
let length = Math.sqrt (rotation[0] * rotation[0] + rotation[1] * rotation[1] + rotation[2] * rotation[2]);
if (length > 0.0) {
let omega = rotation[3] * -0.5;
let si = Math.sin (omega) / length;
result = [si * rotation[0], si * rotation[1], si * rotation[2], Math.cos (omega)];
}
return result;
}
if (node.rotations.length === 0) {
return [0.0, 0.0, 0.0, 1.0];
}
let rotation = node.rotations[0];
return GetQuaternionFromAxisAndAngle (rotation);
}
function GetNodeScale (node)
{
if (node.scales.length === 0) {
return [1.0, 1.0, 1.0];
}
return node.scales[0];
}
if (node.matrix !== null) {
return node.matrix;
}
let matrix = new OV.Matrix ();
matrix.ComposeTRS (
GetNodePosition (node),
GetNodeRotation (node),
GetNodeScale (node)
);
if (node.userId !== 65535) {
let parentIndex = nodeHierarchy.idToIndex[node.userId];
if (parentIndex !== undefined) {
let parentNode = nodeHierarchy.nodes[parentIndex];
let parentMatrix = GetNodeTransformation (nodeHierarchy, parentNode);
matrix = matrix.MultiplyMatrix (parentMatrix);
}
}
node.matrix = matrix;
return matrix;
}
function ApplyMeshTransformation (model, currentMeshIndex, meshMatrix, nodeHierarchy, node)
{
function GetNodePivotPoint (node)
{
if (node === null) {
return [0.0, 0.0, 0.0];
}
return node.pivot;
}
if (!meshMatrix.IsValid ()) {
return;
}
let nodeMatrix = meshMatrix;
if (node !== null) {
nodeMatrix = GetNodeTransformation (nodeHierarchy, node);
}
let determinant = meshMatrix.Determinant ();
if (OV.IsNegative (determinant)) {
// Mirror by x coordinates
let scaleMatrix = new OV.Matrix ().CreateScale (-1.0, 1.0, 1.0);
meshMatrix = scaleMatrix.MultiplyMatrix (meshMatrix);
}
let invMeshMatrix = meshMatrix.Invert ();
if (invMeshMatrix === null) {
return;
}
let pivotPoint = GetNodePivotPoint (node);
let pivotMatrix = new OV.Matrix ().CreateTranslation (-pivotPoint[0], -pivotPoint[1], -pivotPoint[2]);
let matrix = nodeMatrix.Clone ();
matrix = pivotMatrix.MultiplyMatrix (matrix);
matrix = invMeshMatrix.MultiplyMatrix (matrix);
let transformation = new OV.Transformation (matrix);
let mesh = model.GetMesh (currentMeshIndex);
OV.TransformMesh (mesh, transformation);
}
function AddDuplicatedMesh (model, meshIndex, toIndex)
{
let mesh = model.GetMesh (meshIndex);
let clonedMesh = OV.CloneMesh (mesh);
let clonedMeshIndex = model.AddMeshToIndex (clonedMesh, toIndex);
return clonedMeshIndex;
}
let newToOldMeshIndexOffset = 0;
for (let meshIndex = 0; meshIndex < this.model.MeshCount (); meshIndex++) {
let currentMeshIndex = meshIndex;
let originalMeshIndex = currentMeshIndex - newToOldMeshIndexOffset;
let meshTransformation = this.meshTransformations[originalMeshIndex];
let meshNodes = nodeHierarchy.meshIndexToNodes[originalMeshIndex];
if (meshNodes === undefined) {
ApplyMeshTransformation (this.model, currentMeshIndex, meshTransformation, nodeHierarchy, null);
} else {
for (let nodeIndex = 0; nodeIndex < meshNodes.length; nodeIndex++) {
let currentNode = meshNodes[nodeIndex];
let transformedMeshIndex = currentMeshIndex;
if (nodeIndex > 0) {
transformedMeshIndex = AddDuplicatedMesh (this.model, currentMeshIndex, currentMeshIndex + nodeIndex);
newToOldMeshIndexOffset += 1;
meshIndex += 1;
}
ApplyMeshTransformation (this.model, transformedMeshIndex, meshTransformation, nodeHierarchy, currentNode);
}
}
}
}
ReadObjectNodeChunk (nodeHierarchy, reader, length)
{
function ReadTrackVector (obj, reader, type)
{
let result = [];
reader.Skip (10);
let keyNum = reader.ReadInteger32 ();
for (let i = 0; i < keyNum; i++) {
reader.ReadInteger32 ();
let flags = reader.ReadUnsignedInteger16 ();
if (flags !== 0) {
reader.ReadFloat32 ();
}
let current = null;
if (type === OV.CHUNK3DS.OBJECT_ROTATION) {
let tmp = reader.ReadFloat32 ();
current = obj.ReadVector (reader);
current[3] = tmp;
} else {
current = obj.ReadVector (reader);
}
result.push (current);
}
return result;
}
let objectNode = {
name : '',
instanceName : '',
nodeId : -1,
flags : -1,
userId : -1,
pivot : [0.0, 0.0, 0.0],
positions : [],
rotations : [],
scales : [],
matrix : null
};
let obj = this;
let endByte = this.GetChunkEnd (reader, length);
this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
if (chunkId === OV.CHUNK3DS.OBJECT_HIERARCHY) {
objectNode.name = obj.ReadName (reader);
objectNode.flags = reader.ReadUnsignedInteger32 ();
objectNode.userId = reader.ReadUnsignedInteger16 ();
} else if (chunkId === OV.CHUNK3DS.OBJECT_INSTANCE_NAME) {
objectNode.instanceName = obj.ReadName (reader);
} else if (chunkId === OV.CHUNK3DS.OBJECT_PIVOT) {
objectNode.pivot = obj.ReadVector (reader);
} else if (chunkId === OV.CHUNK3DS.OBJECT_POSITION) {
objectNode.positions = ReadTrackVector (obj, reader, OV.CHUNK3DS.OBJECT_POSITION);
} else if (chunkId === OV.CHUNK3DS.OBJECT_ROTATION) {
objectNode.rotations = ReadTrackVector (obj, reader, OV.CHUNK3DS.OBJECT_ROTATION);
} else if (chunkId === OV.CHUNK3DS.OBJECT_SCALE) {
objectNode.scales = ReadTrackVector (obj, reader, OV.CHUNK3DS.OBJECT_SCALE);
} else if (chunkId === OV.CHUNK3DS.OBJECT_ID) {
objectNode.nodeId = reader.ReadUnsignedInteger16 ();
} else {
obj.SkipChunk (reader, chunkLength);
}
});
let nodeIndex = nodeHierarchy.nodes.length;
nodeHierarchy.nodes.push (objectNode);
nodeHierarchy.idToIndex[objectNode.nodeId] = nodeIndex;
let meshIndex = this.meshNameToIndex[objectNode.name];
if (meshIndex !== undefined) {
let meshNodes = nodeHierarchy.meshIndexToNodes[meshIndex];
if (meshNodes === undefined) {
nodeHierarchy.meshIndexToNodes[meshIndex] = [];
}
nodeHierarchy.meshIndexToNodes[meshIndex].push (objectNode);
}
}
ReadName (reader)
{
let name = '';
let char = 0;
let count = 0;
while (count < 64) {
char = reader.ReadCharacter8 ();
if (char === 0) {
break;
}
name = name + String.fromCharCode (char);
count = count + 1;
}
return name;
}
ReadVector (reader)
{
let result = [
reader.ReadFloat32 (),
reader.ReadFloat32 (),
reader.ReadFloat32 ()
];
return result;
}
ReadChunks (reader, endByte, onChunk)
{
while (reader.GetPosition () <= endByte - 6) {
let chunkId = reader.ReadUnsignedInteger16 ();
let chunkLength = reader.ReadUnsignedInteger32 ();
onChunk (chunkId, chunkLength);
}
}
GetChunkEnd (reader, length)
{
return reader.GetPosition () + length - 6;
}
SkipChunk (reader, length)
{
reader.Skip (length - 6);
}
};