Add code for new version.
This commit is contained in:
parent
6eb0a65645
commit
9264c1dd6d
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
build/*
|
||||
website_meta_data.txt
|
||||
website_analytics_data.txt
|
||||
website_script_data.txt
|
||||
package-lock.json
|
||||
node_modules
|
||||
__pycache__
|
||||
39
.jshintrc
Normal file
39
.jshintrc
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"esversion" : 6,
|
||||
"curly" : true,
|
||||
"forin" : true,
|
||||
"latedef" : true,
|
||||
"nonew" : true,
|
||||
"quotmark" : true,
|
||||
"funcscope" : true,
|
||||
"lastsemic" : true,
|
||||
"loopfunc" : true,
|
||||
"shadow" : false,
|
||||
"undef" : true,
|
||||
"unused" : false,
|
||||
"bitwise" : false,
|
||||
"freeze" : true,
|
||||
"leanswitch" : true,
|
||||
"eqeqeq" : true,
|
||||
"futurehostile" : true,
|
||||
"regexpu" : true,
|
||||
"varstmt" : true,
|
||||
"globals" : {
|
||||
"OV" : true,
|
||||
"THREE" : false,
|
||||
"TextDecoder" : false,
|
||||
"XMLHttpRequest" : false,
|
||||
"FileReader" : false,
|
||||
"Blob" : false,
|
||||
"URL" : false,
|
||||
"window" : false,
|
||||
"document" : false,
|
||||
"setTimeout" : false,
|
||||
"requestAnimationFrame" : false,
|
||||
"getComputedStyle" : false,
|
||||
"alert" : false,
|
||||
"atob" : false,
|
||||
"console" : false,
|
||||
"$" : false
|
||||
}
|
||||
}
|
||||
21
LICENSE.md
Normal file
21
LICENSE.md
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Viktor Kovacs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
41
README.md
Normal file
41
README.md
Normal file
@ -0,0 +1,41 @@
|
||||
# Online 3D Viewer
|
||||
|
||||
This repository contains the source code of the https://3dviewer.net website. The website can open several 3D file formats and visualize the model in your browser.
|
||||
|
||||
## Supported file formats
|
||||
|
||||
### Import
|
||||
|
||||
- obj (with mtl and texture)
|
||||
- 3ds (with texture)
|
||||
- stl (ascii and binary)
|
||||
- ply (ascii and binary)
|
||||
- gltf (ascii and binary)
|
||||
- off (ascii only)
|
||||
|
||||
### Export
|
||||
|
||||
- obj (with mtl)
|
||||
- stl (ascii and binary)
|
||||
- ply (ascii and binary)
|
||||
- off (ascii only)
|
||||
|
||||
## Features
|
||||
|
||||
- Load model:
|
||||
- Select files from a file browser dialog
|
||||
- Drag and drop files from your computer
|
||||
- Specify files by web url
|
||||
- Specify files by web url in hash parameters
|
||||
- Explore model:
|
||||
- Orbit, pan, zoom
|
||||
- Set up direction
|
||||
- Fit to window
|
||||
- Investigate model:
|
||||
- List used and missing files
|
||||
- List all materials and meshes
|
||||
- Show/hide and zoom to a specific mesh
|
||||
- List materials used by a specific mesh
|
||||
- Show model information (model size, vertex and polygon count)
|
||||
- Export model to various format
|
||||
- Embed viewer in your website
|
||||
1
StartServer.bat
Normal file
1
StartServer.bat
Normal file
@ -0,0 +1 @@
|
||||
python -m http.server 8000
|
||||
2
libs/jquery-3.5.1.min.js
vendored
Normal file
2
libs/jquery-3.5.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
20
libs/jquery.license.txt
Normal file
20
libs/jquery.license.txt
Normal file
@ -0,0 +1,20 @@
|
||||
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
21
libs/three.license.txt
Normal file
21
libs/three.license.txt
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License
|
||||
|
||||
Copyright © 2010-2021 three.js authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
2
libs/three.min-126.js
Normal file
2
libs/three.min-126.js
Normal file
File diff suppressed because one or more lines are too long
19
package.json
Normal file
19
package.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "online-3d-viewer",
|
||||
"description": "Online 3D Viewer",
|
||||
"version": "0.7.0",
|
||||
"repository": "github:kovacsv/Online3DViewer",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"google-closure-compiler": "^20210302.0.0",
|
||||
"jshint": "^2.12.0",
|
||||
"mocha": "^8.3.2",
|
||||
"svgo": "^2.2.2"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha test",
|
||||
"build": "python tools/create_package.py",
|
||||
"update": "python tools/update_includes.py",
|
||||
"svg": "python tools/optimize_svg_files.py"
|
||||
}
|
||||
}
|
||||
4
source/core/core.js
Normal file
4
source/core/core.js
Normal file
@ -0,0 +1,4 @@
|
||||
OV =
|
||||
{
|
||||
|
||||
};
|
||||
41
source/core/taskrunner.js
Normal file
41
source/core/taskrunner.js
Normal file
@ -0,0 +1,41 @@
|
||||
OV.TaskRunner = class
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
this.count = null;
|
||||
this.current = null;
|
||||
this.callbacks = null;
|
||||
}
|
||||
|
||||
Run (count, callbacks)
|
||||
{
|
||||
this.count = count;
|
||||
this.current = 0;
|
||||
this.callbacks = callbacks;
|
||||
if (count === 0) {
|
||||
this.TaskReady ();
|
||||
} else {
|
||||
this.RunOnce ();
|
||||
}
|
||||
}
|
||||
|
||||
RunOnce ()
|
||||
{
|
||||
let obj = this;
|
||||
setTimeout (function () {
|
||||
obj.callbacks.runTask (obj.current, obj.TaskReady.bind (obj));
|
||||
}, 0);
|
||||
}
|
||||
|
||||
TaskReady ()
|
||||
{
|
||||
this.current += 1;
|
||||
if (this.current < this.count) {
|
||||
this.RunOnce ();
|
||||
} else {
|
||||
if (this.callbacks.onReady) {
|
||||
this.callbacks.onReady ();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
26
source/export/exporter.js
Normal file
26
source/export/exporter.js
Normal file
@ -0,0 +1,26 @@
|
||||
OV.Exporter = class
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
this.exporters = [
|
||||
new OV.ExporterObj (),
|
||||
new OV.ExporterStl (),
|
||||
new OV.ExporterPly (),
|
||||
new OV.ExporterOff (),
|
||||
new OV.ExporterGltf ()
|
||||
];
|
||||
}
|
||||
|
||||
Export (model, format, extension, callbacks)
|
||||
{
|
||||
let files = [];
|
||||
for (let i = 0; i < this.exporters.length; i++) {
|
||||
let exporter = this.exporters[i];
|
||||
if (exporter.CanExport (format, extension)) {
|
||||
exporter.Export (model, format, files, callbacks);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
};
|
||||
81
source/export/exporterbase.js
Normal file
81
source/export/exporterbase.js
Normal file
@ -0,0 +1,81 @@
|
||||
OV.ExportedFile = class
|
||||
{
|
||||
constructor (name)
|
||||
{
|
||||
this.name = name;
|
||||
this.url = null;
|
||||
this.content = null;
|
||||
}
|
||||
|
||||
GetName ()
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
|
||||
SetName (name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
GetUrl ()
|
||||
{
|
||||
return this.url;
|
||||
}
|
||||
|
||||
SetUrl (url)
|
||||
{
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
GetContent ()
|
||||
{
|
||||
return this.content;
|
||||
}
|
||||
|
||||
SetContent (content)
|
||||
{
|
||||
this.content = content;
|
||||
}
|
||||
};
|
||||
|
||||
OV.ExporterBase = class
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
this.callbacks = null;
|
||||
}
|
||||
|
||||
CanExport (format, extension)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Export (model, format, files, callbacks)
|
||||
{
|
||||
this.callbacks = callbacks;
|
||||
this.ExportContent (model, format, files);
|
||||
}
|
||||
|
||||
ExportContent (model, format, files)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
GetExportedMaterialName (originalName)
|
||||
{
|
||||
return this.GetExportedName (originalName, 'Material');
|
||||
}
|
||||
|
||||
GetExportedMeshName (originalName)
|
||||
{
|
||||
return this.GetExportedName (originalName, 'Mesh');
|
||||
}
|
||||
|
||||
GetExportedName (originalName, defaultName)
|
||||
{
|
||||
if (originalName.length === 0) {
|
||||
return defaultName;
|
||||
}
|
||||
return originalName;
|
||||
}
|
||||
};
|
||||
404
source/export/exportergltf.js
Normal file
404
source/export/exportergltf.js
Normal file
@ -0,0 +1,404 @@
|
||||
OV.ExporterGltf = class extends OV.ExporterBase
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
super ();
|
||||
this.components = {
|
||||
index : {
|
||||
type : 5125, // unsigned int 32
|
||||
size : 4
|
||||
},
|
||||
number : {
|
||||
type : 5126, // float 32
|
||||
size : 4
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
CanExport (format, extension)
|
||||
{
|
||||
return (format === OV.FileFormat.Text && extension === 'gltf') || (format === OV.FileFormat.Binary && extension === 'glb');
|
||||
}
|
||||
|
||||
ExportContent (model, format, files)
|
||||
{
|
||||
if (format === OV.FileFormat.Text) {
|
||||
this.ExportAsciiContent (model, files);
|
||||
} else if (format === OV.FileFormat.Binary) {
|
||||
this.ExportBinaryContent (model, files);
|
||||
}
|
||||
}
|
||||
|
||||
ExportAsciiContent (model, files)
|
||||
{
|
||||
let gltfFile = new OV.ExportedFile ('model.gltf');
|
||||
let binFile = new OV.ExportedFile ('model.bin');
|
||||
files.push (gltfFile);
|
||||
files.push (binFile);
|
||||
|
||||
let meshDataArr = this.GetMeshData (model);
|
||||
let mainBuffer = this.GetMainBuffer (meshDataArr);
|
||||
let mainJson = this.GetMainJson (meshDataArr);
|
||||
mainJson.buffers.push ({
|
||||
uri : binFile.GetName (),
|
||||
byteLength : mainBuffer.byteLength
|
||||
});
|
||||
|
||||
let fileNameToIndex = [];
|
||||
this.ExportMaterials (model, mainJson, function (texture) {
|
||||
let fileName = OV.GetFileName (texture.name);
|
||||
let textureIndex = fileNameToIndex[fileName];
|
||||
if (textureIndex === undefined) {
|
||||
let textureFile = new OV.ExportedFile (fileName);
|
||||
textureFile.SetUrl (texture.url);
|
||||
files.push (textureFile);
|
||||
|
||||
textureIndex = mainJson.textures.length;
|
||||
fileNameToIndex[fileName] = textureIndex;
|
||||
|
||||
mainJson.images.push ({
|
||||
uri : fileName
|
||||
});
|
||||
mainJson.textures.push ({
|
||||
source : textureIndex
|
||||
});
|
||||
}
|
||||
return textureIndex;
|
||||
});
|
||||
|
||||
gltfFile.SetContent (JSON.stringify (mainJson, null, 4));
|
||||
binFile.SetContent (mainBuffer);
|
||||
}
|
||||
|
||||
ExportBinaryContent (model, files)
|
||||
{
|
||||
let glbFile = new OV.ExportedFile ('model.glb');
|
||||
files.push (glbFile);
|
||||
|
||||
let meshDataArr = this.GetMeshData (model);
|
||||
let mainBuffer = this.GetMainBuffer (meshDataArr);
|
||||
let mainJson = this.GetMainJson (meshDataArr);
|
||||
mainJson.buffers.push ({
|
||||
byteLength : mainBuffer.byteLength
|
||||
});
|
||||
|
||||
let textureBuffers = [];
|
||||
let obj = this;
|
||||
|
||||
let fileNameToIndex = [];
|
||||
this.ExportMaterials (model, mainJson, function (texture) {
|
||||
let fileName = OV.GetFileName (texture.name);
|
||||
let extension = OV.GetFileExtension (texture.name);
|
||||
let textureIndex = fileNameToIndex[fileName];
|
||||
if (textureIndex === undefined) {
|
||||
let bufferIndex = mainJson.buffers.length;
|
||||
let bufferViewIndex = mainJson.bufferViews.length;
|
||||
textureIndex = mainJson.textures.length;
|
||||
fileNameToIndex[fileName] = textureIndex;
|
||||
let textureBuffer = obj.callbacks.getTextureBuffer (texture.name);
|
||||
textureBuffers.push (textureBuffer);
|
||||
mainJson.buffers.push ({
|
||||
byteLength : textureBuffer.byteLength
|
||||
});
|
||||
mainJson.bufferViews.push ({
|
||||
buffer : bufferIndex,
|
||||
byteOffset : 0,
|
||||
byteLength : textureBuffer.byteLength
|
||||
});
|
||||
mainJson.images.push ({
|
||||
bufferView : bufferViewIndex,
|
||||
mimeType : 'image/' + extension
|
||||
});
|
||||
mainJson.textures.push ({
|
||||
source : textureIndex
|
||||
});
|
||||
}
|
||||
return textureIndex;
|
||||
});
|
||||
|
||||
let mainJsonString = JSON.stringify (mainJson, null, 4);
|
||||
let glbSize = 12 + 8 + mainJsonString.length;
|
||||
for (let i = 0; i < mainJson.buffers.length; i++) {
|
||||
glbSize += 8 + mainJson.buffers[i].byteLength;
|
||||
}
|
||||
|
||||
let glbWriter = new OV.BinaryWriter (glbSize, true);
|
||||
|
||||
glbWriter.WriteUnsignedInteger32 (0x46546C67);
|
||||
glbWriter.WriteUnsignedInteger32 (2);
|
||||
glbWriter.WriteUnsignedInteger32 (glbSize);
|
||||
|
||||
glbWriter.WriteUnsignedInteger32 (mainJsonString.length);
|
||||
glbWriter.WriteUnsignedInteger32 (0x4E4F534A);
|
||||
for (let i = 0; i < mainJsonString.length; i++) {
|
||||
glbWriter.WriteUnsignedCharacter8 (mainJsonString.charCodeAt (i));
|
||||
}
|
||||
|
||||
glbWriter.WriteUnsignedInteger32 (mainBuffer.byteLength);
|
||||
glbWriter.WriteUnsignedInteger32 (0x004E4942);
|
||||
glbWriter.WriteArrayBuffer (mainBuffer);
|
||||
|
||||
for (let i = 0; i < textureBuffers.length; i++) {
|
||||
let textureBuffer = textureBuffers[i];
|
||||
glbWriter.WriteUnsignedInteger32 (textureBuffer.byteLength);
|
||||
glbWriter.WriteUnsignedInteger32 (0x004E4942);
|
||||
glbWriter.WriteArrayBuffer (textureBuffer);
|
||||
}
|
||||
|
||||
glbFile.SetContent (glbWriter.GetBuffer ());
|
||||
}
|
||||
|
||||
GetMeshData (model)
|
||||
{
|
||||
let meshDataArr = [];
|
||||
|
||||
for (let meshIndex = 0; meshIndex < model.MeshCount (); meshIndex++) {
|
||||
let mesh = model.GetMesh (meshIndex);
|
||||
let buffer = OV.ConvertMeshToMeshBuffer (mesh);
|
||||
meshDataArr.push ({
|
||||
name : mesh.GetName (),
|
||||
buffer : buffer,
|
||||
offsets : [],
|
||||
sizes : []
|
||||
});
|
||||
}
|
||||
|
||||
return meshDataArr;
|
||||
}
|
||||
|
||||
GetMainBuffer (meshDataArr)
|
||||
{
|
||||
let mainBufferSize = 0;
|
||||
for (let meshIndex = 0; meshIndex < meshDataArr.length; meshIndex++) {
|
||||
let meshData = meshDataArr[meshIndex];
|
||||
mainBufferSize += meshData.buffer.GetByteLength (this.components.index.size, this.components.number.size);
|
||||
}
|
||||
|
||||
let writer = new OV.BinaryWriter (mainBufferSize, true);
|
||||
for (let meshIndex = 0; meshIndex < meshDataArr.length; meshIndex++) {
|
||||
let meshData = meshDataArr[meshIndex];
|
||||
let primitives = meshData.buffer.primitives;
|
||||
for (let primitiveIndex = 0; primitiveIndex < primitives.length; primitiveIndex++) {
|
||||
let primitive = primitives[primitiveIndex];
|
||||
let offset = writer.GetPosition ();
|
||||
for (let i = 0; i < primitive.indices.length; i++) {
|
||||
writer.WriteUnsignedInteger32 (primitive.indices[i]);
|
||||
}
|
||||
for (let i = 0; i < primitive.vertices.length; i++) {
|
||||
writer.WriteFloat32 (primitive.vertices[i]);
|
||||
}
|
||||
for (let i = 0; i < primitive.normals.length; i++) {
|
||||
writer.WriteFloat32 (primitive.normals[i]);
|
||||
}
|
||||
for (let i = 0; i < primitive.uvs.length; i++) {
|
||||
let texCoord = primitive.uvs[i];
|
||||
if (i % 2 === 1) {
|
||||
texCoord *= -1.0;
|
||||
}
|
||||
writer.WriteFloat32 (texCoord);
|
||||
}
|
||||
meshData.offsets.push (offset);
|
||||
meshData.sizes.push (writer.GetPosition () - offset);
|
||||
}
|
||||
}
|
||||
|
||||
return writer.GetBuffer ();
|
||||
}
|
||||
|
||||
GetMainJson (meshDataArr)
|
||||
{
|
||||
let mainJson = {
|
||||
asset : {
|
||||
version : '2.0'
|
||||
},
|
||||
scene : 0,
|
||||
scenes : [
|
||||
{
|
||||
nodes : []
|
||||
}
|
||||
],
|
||||
nodes : [],
|
||||
materials : [],
|
||||
meshes : [],
|
||||
buffers : [],
|
||||
bufferViews : [],
|
||||
accessors : []
|
||||
};
|
||||
|
||||
for (let meshIndex = 0; meshIndex < meshDataArr.length; meshIndex++) {
|
||||
let meshData = meshDataArr[meshIndex];
|
||||
mainJson.scenes[0].nodes.push (meshIndex);
|
||||
mainJson.nodes.push ({
|
||||
mesh : meshIndex
|
||||
});
|
||||
let jsonMesh = {
|
||||
name : this.GetExportedMeshName (meshData.name),
|
||||
primitives : []
|
||||
};
|
||||
|
||||
let primitives = meshData.buffer.primitives;
|
||||
for (let primitiveIndex = 0; primitiveIndex < primitives.length; primitiveIndex++) {
|
||||
let primitive = primitives[primitiveIndex];
|
||||
let bufferViewIndex = mainJson.bufferViews.length;
|
||||
let accessorIndex = mainJson.accessors.length;
|
||||
let accessorOffset = 0;
|
||||
|
||||
let jsonPrimitive = {
|
||||
attributes : {
|
||||
POSITION : accessorIndex + 1,
|
||||
NORMAL : accessorIndex + 2
|
||||
},
|
||||
indices : accessorIndex,
|
||||
mode : 4,
|
||||
material : primitive.material
|
||||
};
|
||||
|
||||
mainJson.bufferViews.push ({
|
||||
buffer : 0,
|
||||
byteOffset : meshData.offsets[primitiveIndex],
|
||||
byteLength : meshData.sizes[primitiveIndex]
|
||||
});
|
||||
|
||||
mainJson.accessors.push ({
|
||||
bufferView : bufferViewIndex,
|
||||
byteOffset : accessorOffset,
|
||||
componentType : this.components.index.type,
|
||||
count : primitive.indices.length,
|
||||
type : 'SCALAR'
|
||||
});
|
||||
accessorOffset += primitive.indices.length * this.components.index.size;
|
||||
|
||||
mainJson.accessors.push ({
|
||||
bufferView : bufferViewIndex,
|
||||
byteOffset : accessorOffset,
|
||||
componentType : this.components.number.type,
|
||||
count : primitive.vertices.length / 3,
|
||||
type : 'VEC3'
|
||||
});
|
||||
accessorOffset += primitive.vertices.length * this.components.number.size;
|
||||
|
||||
mainJson.accessors.push ({
|
||||
bufferView : bufferViewIndex,
|
||||
byteOffset : accessorOffset,
|
||||
componentType : this.components.number.type,
|
||||
count : primitive.normals.length / 3,
|
||||
type : 'VEC3'
|
||||
});
|
||||
accessorOffset += primitive.normals.length * this.components.number.size;
|
||||
|
||||
if (primitive.uvs.length > 0) {
|
||||
mainJson.accessors.push ({
|
||||
bufferView : bufferViewIndex,
|
||||
byteOffset : accessorOffset,
|
||||
componentType : this.components.number.type,
|
||||
count : primitive.uvs.length / 2,
|
||||
type : 'VEC2'
|
||||
});
|
||||
accessorOffset += primitive.uvs.length * this.components.number.size;
|
||||
jsonPrimitive.attributes.TEXCOORD_0 = accessorIndex + 3;
|
||||
}
|
||||
|
||||
jsonMesh.primitives.push (jsonPrimitive);
|
||||
}
|
||||
mainJson.meshes.push (jsonMesh);
|
||||
}
|
||||
|
||||
return mainJson;
|
||||
}
|
||||
|
||||
ExportMaterials (model, mainJson, addTexture)
|
||||
{
|
||||
function ExportMaterial (obj, mainJson, material, addTexture)
|
||||
{
|
||||
function ColorToRGBA (color, opacity)
|
||||
{
|
||||
return [
|
||||
OV.SRGBToLinear (color.r / 255.0),
|
||||
OV.SRGBToLinear (color.g / 255.0),
|
||||
OV.SRGBToLinear (color.b / 255.0),
|
||||
opacity
|
||||
];
|
||||
}
|
||||
|
||||
function ColorToRGB (color)
|
||||
{
|
||||
return [
|
||||
OV.SRGBToLinear (color.r / 255.0),
|
||||
OV.SRGBToLinear (color.g / 255.0),
|
||||
OV.SRGBToLinear (color.b / 255.0)
|
||||
];
|
||||
}
|
||||
|
||||
function GetTextureParams (mainJson, texture, addTexture)
|
||||
{
|
||||
if (texture === null || texture.name === null || texture.url === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (mainJson.images === undefined) {
|
||||
mainJson.images = [];
|
||||
}
|
||||
if (mainJson.textures === undefined) {
|
||||
mainJson.textures = [];
|
||||
}
|
||||
|
||||
let textureIndex = addTexture (texture);
|
||||
let textureParams = {
|
||||
index : textureIndex
|
||||
};
|
||||
|
||||
if (texture.HasTransformation ()) {
|
||||
textureParams.extensions = {
|
||||
KHR_texture_transform : {
|
||||
offset : [texture.offset.x, -texture.offset.y],
|
||||
scale : [texture.scale.x, texture.scale.y],
|
||||
rotation : -texture.rotation
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return textureParams;
|
||||
}
|
||||
|
||||
let jsonMaterial = {
|
||||
name : obj.GetExportedMaterialName (material.name),
|
||||
pbrMetallicRoughness : {
|
||||
baseColorFactor : ColorToRGBA (material.diffuse, material.opacity),
|
||||
metallicFactor : 0.0,
|
||||
roughnessFactor : 1.0
|
||||
},
|
||||
emissiveFactor : ColorToRGB (material.emissive),
|
||||
alphaMode : 'OPAQUE',
|
||||
alphaCutoff : material.alphaTest
|
||||
};
|
||||
|
||||
if (material.transparent) {
|
||||
// TODO: mask?
|
||||
jsonMaterial.alphaMode = 'BLEND';
|
||||
}
|
||||
|
||||
let baseColorTexture = GetTextureParams (mainJson, material.diffuseMap, addTexture);
|
||||
if (baseColorTexture !== null) {
|
||||
if (!material.multiplyDiffuseMap) {
|
||||
jsonMaterial.pbrMetallicRoughness.baseColorFactor = ColorToRGBA (new OV.Color (255, 255, 255), material.opacity);
|
||||
}
|
||||
jsonMaterial.pbrMetallicRoughness.baseColorTexture = baseColorTexture;
|
||||
}
|
||||
let normalTexture = GetTextureParams (mainJson, material.normalMap, addTexture);
|
||||
if (normalTexture !== null) {
|
||||
jsonMaterial.normalTexture = normalTexture;
|
||||
}
|
||||
let emissiveTexture = GetTextureParams (mainJson, material.emissiveMap, addTexture);
|
||||
if (emissiveTexture !== null) {
|
||||
jsonMaterial.emissiveTexture = emissiveTexture;
|
||||
}
|
||||
|
||||
mainJson.materials.push (jsonMaterial);
|
||||
}
|
||||
|
||||
for (let materialIndex = 0; materialIndex < model.MaterialCount (); materialIndex++) {
|
||||
let material = model.GetMaterial (materialIndex);
|
||||
ExportMaterial (this, mainJson, material, addTexture);
|
||||
}
|
||||
}
|
||||
};
|
||||
107
source/export/exporterobj.js
Normal file
107
source/export/exporterobj.js
Normal file
@ -0,0 +1,107 @@
|
||||
OV.ExporterObj = class extends OV.ExporterBase
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
super ();
|
||||
}
|
||||
|
||||
CanExport (format, extension)
|
||||
{
|
||||
return format === OV.FileFormat.Text && extension === 'obj';
|
||||
}
|
||||
|
||||
ExportContent (model, format, files)
|
||||
{
|
||||
function WriteTexture (mtlWriter, keyword, texture, files)
|
||||
{
|
||||
if (texture !== null && texture.name !== null && texture.url !== null) {
|
||||
let fileName = OV.GetFileName (texture.name);
|
||||
mtlWriter.WriteArrayLine ([keyword, fileName]);
|
||||
|
||||
let fileIndex = files.findIndex (function (file) {
|
||||
return file.GetName () === fileName;
|
||||
});
|
||||
if (fileIndex === -1) {
|
||||
let textureFile = new OV.ExportedFile (fileName);
|
||||
textureFile.SetUrl (texture.url);
|
||||
files.push (textureFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mtlFile = new OV.ExportedFile ('model.mtl');
|
||||
let objFile = new OV.ExportedFile ('model.obj');
|
||||
|
||||
files.push (mtlFile);
|
||||
files.push (objFile);
|
||||
|
||||
let mtlWriter = new OV.TextWriter ();
|
||||
for (let materialIndex = 0; materialIndex < model.MaterialCount (); materialIndex++) {
|
||||
let material = model.GetMaterial (materialIndex);
|
||||
mtlWriter.WriteArrayLine (['newmtl', this.GetExportedMaterialName (material.name)]);
|
||||
mtlWriter.WriteArrayLine (['Ka', material.ambient.r / 255.0, material.ambient.g / 255.0, material.ambient.b / 255.0]);
|
||||
mtlWriter.WriteArrayLine (['Kd', material.diffuse.r / 255.0, material.diffuse.g / 255.0, material.diffuse.b / 255.0]);
|
||||
mtlWriter.WriteArrayLine (['Ks', material.specular.r / 255.0, material.specular.g / 255.0, material.specular.b / 255.0]);
|
||||
mtlWriter.WriteArrayLine (['Ns', material.shininess * 100.0]);
|
||||
mtlWriter.WriteArrayLine (['d', material.opacity]);
|
||||
WriteTexture (mtlWriter, 'map_Kd', material.diffuseMap, files);
|
||||
WriteTexture (mtlWriter, 'map_Ks', material.specularMap, files);
|
||||
WriteTexture (mtlWriter, 'bump', material.bumpMap, files);
|
||||
}
|
||||
mtlFile.SetContent (mtlWriter.GetText ());
|
||||
|
||||
let objWriter = new OV.TextWriter ();
|
||||
objWriter.WriteArrayLine (['mtllib', mtlFile.GetName ()]);
|
||||
let vertexOffset = 0;
|
||||
let normalOffset = 0;
|
||||
let uvOffset = 0;
|
||||
let usedMaterialName = null;
|
||||
for (let meshIndex = 0; meshIndex < model.MeshCount (); meshIndex++) {
|
||||
let mesh = model.GetMesh (meshIndex);
|
||||
objWriter.WriteArrayLine (['g', this.GetExportedMeshName (mesh.GetName ())]);
|
||||
for (let vertexIndex = 0; vertexIndex < mesh.VertexCount (); vertexIndex++) {
|
||||
let vertex = mesh.GetVertex (vertexIndex);
|
||||
objWriter.WriteArrayLine (['v', vertex.x, vertex.y, vertex.z]);
|
||||
}
|
||||
for (let normalIndex = 0; normalIndex < mesh.NormalCount (); normalIndex++) {
|
||||
let normal = mesh.GetNormal (normalIndex);
|
||||
objWriter.WriteArrayLine (['vn', normal.x, normal.y, normal.z]);
|
||||
}
|
||||
for (let textureUVIndex = 0; textureUVIndex < mesh.TextureUVCount (); textureUVIndex++) {
|
||||
let uv = mesh.GetTextureUV (textureUVIndex);
|
||||
objWriter.WriteArrayLine (['vt', uv.x, uv.y]);
|
||||
}
|
||||
for (let triangleIndex = 0; triangleIndex < mesh.TriangleCount (); triangleIndex++) {
|
||||
let triangle = mesh.GetTriangle (triangleIndex);
|
||||
let v0 = triangle.v0 + vertexOffset + 1;
|
||||
let v1 = triangle.v1 + vertexOffset + 1;
|
||||
let v2 = triangle.v2 + vertexOffset + 1;
|
||||
let n0 = triangle.n0 + normalOffset + 1;
|
||||
let n1 = triangle.n1 + normalOffset + 1;
|
||||
let n2 = triangle.n2 + normalOffset + 1;
|
||||
if (triangle.mat !== null) {
|
||||
let material = model.GetMaterial (triangle.mat);
|
||||
let materialName = this.GetExportedMaterialName (material.name);
|
||||
if (materialName !== usedMaterialName) {
|
||||
objWriter.WriteArrayLine (['usemtl', materialName]);
|
||||
usedMaterialName = materialName;
|
||||
}
|
||||
}
|
||||
let u0 = '';
|
||||
let u1 = '';
|
||||
let u2 = '';
|
||||
if (triangle.HasTextureUVs ()) {
|
||||
u0 = triangle.u0 + uvOffset + 1;
|
||||
u1 = triangle.u1 + uvOffset + 1;
|
||||
u2 = triangle.u2 + uvOffset + 1;
|
||||
}
|
||||
objWriter.WriteArrayLine (['f', [v0, u0, n0].join ('/'), [v1, u1, n1].join ('/'), [v2, u2, n2].join ('/')]);
|
||||
}
|
||||
vertexOffset += mesh.VertexCount ();
|
||||
normalOffset += mesh.NormalCount ();
|
||||
uvOffset += mesh.TextureUVCount ();
|
||||
}
|
||||
|
||||
objFile.SetContent (objWriter.GetText ());
|
||||
}
|
||||
};
|
||||
33
source/export/exporteroff.js
Normal file
33
source/export/exporteroff.js
Normal file
@ -0,0 +1,33 @@
|
||||
OV.ExporterOff = class extends OV.ExporterBase
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
super ();
|
||||
}
|
||||
|
||||
CanExport (format, extension)
|
||||
{
|
||||
return format === OV.FileFormat.Text && extension === 'off';
|
||||
}
|
||||
|
||||
ExportContent (model, format, files)
|
||||
{
|
||||
let offFile = new OV.ExportedFile ('model.off');
|
||||
files.push (offFile);
|
||||
|
||||
let offWriter = new OV.TextWriter ();
|
||||
offWriter.WriteLine ('OFF');
|
||||
offWriter.WriteArrayLine ([model.VertexCount (), model.TriangleCount (), 0]);
|
||||
|
||||
OV.EnumerateModelVerticesAndTriangles (model, {
|
||||
onVertex : function (x, y, z) {
|
||||
offWriter.WriteArrayLine ([x, y, z]);
|
||||
},
|
||||
onTriangle : function (v0, v1, v2) {
|
||||
offWriter.WriteArrayLine ([3, v0, v1, v2]);
|
||||
}
|
||||
});
|
||||
|
||||
offFile.SetContent (offWriter.GetText ());
|
||||
}
|
||||
};
|
||||
94
source/export/exporterply.js
Normal file
94
source/export/exporterply.js
Normal file
@ -0,0 +1,94 @@
|
||||
OV.ExporterPly = class extends OV.ExporterBase
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
super ();
|
||||
}
|
||||
|
||||
CanExport (format, extension)
|
||||
{
|
||||
return (format === OV.FileFormat.Text || format === OV.FileFormat.Binary) && extension === 'ply';
|
||||
}
|
||||
|
||||
ExportContent (model, format, files)
|
||||
{
|
||||
if (format === OV.FileFormat.Text) {
|
||||
this.ExportText (model, files);
|
||||
} else {
|
||||
this.ExportBinary (model, files);
|
||||
}
|
||||
}
|
||||
|
||||
ExportText (model, files)
|
||||
{
|
||||
let plyFile = new OV.ExportedFile ('model.ply');
|
||||
files.push (plyFile);
|
||||
|
||||
let plyWriter = new OV.TextWriter ();
|
||||
|
||||
let vertexCount = model.VertexCount ();
|
||||
let triangleCount = model.TriangleCount ();
|
||||
let headerText = this.GetHeaderText ('ascii', vertexCount, triangleCount);
|
||||
plyWriter.Write (headerText);
|
||||
|
||||
OV.EnumerateModelVerticesAndTriangles (model, {
|
||||
onVertex : function (x, y, z) {
|
||||
plyWriter.WriteArrayLine ([x, y, z]);
|
||||
},
|
||||
onTriangle : function (v0, v1, v2) {
|
||||
plyWriter.WriteArrayLine ([3, v0, v1, v2]);
|
||||
}
|
||||
});
|
||||
|
||||
plyFile.SetContent (plyWriter.GetText ());
|
||||
}
|
||||
|
||||
ExportBinary (model, files)
|
||||
{
|
||||
let plyFile = new OV.ExportedFile ('model.ply');
|
||||
files.push (plyFile);
|
||||
|
||||
|
||||
let vertexCount = model.VertexCount ();
|
||||
let triangleCount = model.TriangleCount ();
|
||||
let headerText = this.GetHeaderText ('binary_little_endian', vertexCount, triangleCount);
|
||||
|
||||
let fullByteLength = headerText.length + vertexCount * 3 * 4 + triangleCount * (1 + 3 * 4);
|
||||
let plyWriter = new OV.BinaryWriter (fullByteLength, true);
|
||||
|
||||
for (let i = 0; i < headerText.length; i++) {
|
||||
plyWriter.WriteUnsignedCharacter8 (headerText.charCodeAt (i));
|
||||
}
|
||||
|
||||
OV.EnumerateModelVerticesAndTriangles (model, {
|
||||
onVertex : function (x, y, z) {
|
||||
plyWriter.WriteFloat32 (x);
|
||||
plyWriter.WriteFloat32 (y);
|
||||
plyWriter.WriteFloat32 (z);
|
||||
},
|
||||
onTriangle : function (v0, v1, v2) {
|
||||
plyWriter.WriteUnsignedCharacter8 (3);
|
||||
plyWriter.WriteInteger32 (v0);
|
||||
plyWriter.WriteInteger32 (v1);
|
||||
plyWriter.WriteInteger32 (v2);
|
||||
}
|
||||
});
|
||||
|
||||
plyFile.SetContent (plyWriter.GetBuffer ());
|
||||
}
|
||||
|
||||
GetHeaderText (format, vertexCount, triangleCount)
|
||||
{
|
||||
let headerWriter = new OV.TextWriter ();
|
||||
headerWriter.WriteLine ('ply');
|
||||
headerWriter.WriteLine ('format ' + format + ' 1.0');
|
||||
headerWriter.WriteLine ('element vertex ' + vertexCount);
|
||||
headerWriter.WriteLine ('property float x');
|
||||
headerWriter.WriteLine ('property float y');
|
||||
headerWriter.WriteLine ('property float z');
|
||||
headerWriter.WriteLine ('element face ' + triangleCount);
|
||||
headerWriter.WriteLine ('property list uchar int vertex_index');
|
||||
headerWriter.WriteLine ('end_header');
|
||||
return headerWriter.GetText ();
|
||||
}
|
||||
};
|
||||
84
source/export/exporterstl.js
Normal file
84
source/export/exporterstl.js
Normal file
@ -0,0 +1,84 @@
|
||||
OV.ExporterStl = class extends OV.ExporterBase
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
super ();
|
||||
}
|
||||
|
||||
CanExport (format, extension)
|
||||
{
|
||||
return (format === OV.FileFormat.Text || format === OV.FileFormat.Binary) && extension === 'stl';
|
||||
}
|
||||
|
||||
ExportContent (model, format, files)
|
||||
{
|
||||
if (format === OV.FileFormat.Text) {
|
||||
this.ExportText (model, files);
|
||||
} else {
|
||||
this.ExportBinary (model, files);
|
||||
}
|
||||
}
|
||||
|
||||
ExportText (model, files)
|
||||
{
|
||||
let stlFile = new OV.ExportedFile ('model.stl');
|
||||
files.push (stlFile);
|
||||
|
||||
let stlWriter = new OV.TextWriter ();
|
||||
stlWriter.WriteLine ('solid Model');
|
||||
OV.EnumerateModelTrianglesWithNormals (model, function (v0, v1, v2, normal) {
|
||||
stlWriter.WriteArrayLine (['facet', 'normal', normal.x, normal.y, normal.z]);
|
||||
stlWriter.Indent (1);
|
||||
stlWriter.WriteLine ('outer loop');
|
||||
stlWriter.Indent (1);
|
||||
stlWriter.WriteArrayLine (['vertex', v0.x, v0.y, v0.z]);
|
||||
stlWriter.WriteArrayLine (['vertex', v1.x, v1.y, v1.z]);
|
||||
stlWriter.WriteArrayLine (['vertex', v2.x, v2.y, v2.z]);
|
||||
stlWriter.Indent (-1);
|
||||
stlWriter.WriteLine ('endloop');
|
||||
stlWriter.Indent (-1);
|
||||
stlWriter.WriteLine ('endfacet');
|
||||
});
|
||||
stlWriter.WriteLine ('endsolid Model');
|
||||
|
||||
stlFile.SetContent (stlWriter.GetText ());
|
||||
}
|
||||
|
||||
ExportBinary (model, files)
|
||||
{
|
||||
let stlFile = new OV.ExportedFile ('model.stl');
|
||||
files.push (stlFile);
|
||||
|
||||
let triangleCount = model.TriangleCount ();
|
||||
let headerSize = 80;
|
||||
let fullByteLength = headerSize + 4 + triangleCount * 50;
|
||||
let stlWriter = new OV.BinaryWriter (fullByteLength, true);
|
||||
|
||||
for (let i = 0; i < headerSize; i++) {
|
||||
stlWriter.WriteUnsignedCharacter8 (0);
|
||||
}
|
||||
|
||||
stlWriter.WriteUnsignedInteger32 (triangleCount);
|
||||
OV.EnumerateModelTrianglesWithNormals (model, function (v0, v1, v2, normal) {
|
||||
stlWriter.WriteFloat32 (normal.x);
|
||||
stlWriter.WriteFloat32 (normal.y);
|
||||
stlWriter.WriteFloat32 (normal.z);
|
||||
|
||||
stlWriter.WriteFloat32 (v0.x);
|
||||
stlWriter.WriteFloat32 (v0.y);
|
||||
stlWriter.WriteFloat32 (v0.z);
|
||||
|
||||
stlWriter.WriteFloat32 (v1.x);
|
||||
stlWriter.WriteFloat32 (v1.y);
|
||||
stlWriter.WriteFloat32 (v1.z);
|
||||
|
||||
stlWriter.WriteFloat32 (v2.x);
|
||||
stlWriter.WriteFloat32 (v2.y);
|
||||
stlWriter.WriteFloat32 (v2.z);
|
||||
|
||||
stlWriter.WriteUnsignedInteger16 (0);
|
||||
});
|
||||
|
||||
stlFile.SetContent (stlWriter.GetBuffer ());
|
||||
}
|
||||
};
|
||||
34
source/export/exporterutils.js
Normal file
34
source/export/exporterutils.js
Normal file
@ -0,0 +1,34 @@
|
||||
OV.EnumerateModelVerticesAndTriangles = function (model, callbacks)
|
||||
{
|
||||
for (let meshIndex = 0; meshIndex < model.MeshCount (); meshIndex++) {
|
||||
let mesh = model.GetMesh (meshIndex);
|
||||
for (let vertexIndex = 0; vertexIndex < mesh.VertexCount (); vertexIndex++) {
|
||||
let vertex = mesh.GetVertex (vertexIndex);
|
||||
callbacks.onVertex (vertex.x, vertex.y, vertex.z);
|
||||
}
|
||||
}
|
||||
let vertexOffset = 0;
|
||||
for (let meshIndex = 0; meshIndex < model.MeshCount (); meshIndex++) {
|
||||
let mesh = model.GetMesh (meshIndex);
|
||||
for (let triangleIndex = 0; triangleIndex < mesh.TriangleCount (); triangleIndex++) {
|
||||
let triangle = mesh.GetTriangle (triangleIndex);
|
||||
callbacks.onTriangle (triangle.v0 + vertexOffset, triangle.v1 + vertexOffset, triangle.v2 + vertexOffset);
|
||||
}
|
||||
vertexOffset += mesh.VertexCount ();
|
||||
}
|
||||
};
|
||||
|
||||
OV.EnumerateModelTrianglesWithNormals = function (model, onTriangle)
|
||||
{
|
||||
for (let meshIndex = 0; meshIndex < model.MeshCount (); meshIndex++) {
|
||||
let mesh = model.GetMesh (meshIndex);
|
||||
for (let triangleIndex = 0; triangleIndex < mesh.TriangleCount (); triangleIndex++) {
|
||||
let triangle = mesh.GetTriangle (triangleIndex);
|
||||
let v0 = mesh.GetVertex (triangle.v0);
|
||||
let v1 = mesh.GetVertex (triangle.v1);
|
||||
let v2 = mesh.GetVertex (triangle.v2);
|
||||
let normal = OV.CalculateTriangleNormal (v0, v1, v2);
|
||||
onTriangle (v0, v1, v2, normal);
|
||||
}
|
||||
}
|
||||
};
|
||||
195
source/external/three.converter.js
vendored
Normal file
195
source/external/three.converter.js
vendored
Normal file
@ -0,0 +1,195 @@
|
||||
OV.ConvertModelToThreeMeshes = function (model, callbacks)
|
||||
{
|
||||
function CreateThreeMaterial (model, materialIndex)
|
||||
{
|
||||
function SetTextureParameters (texture, threeTexture)
|
||||
{
|
||||
threeTexture.wrapS = THREE.RepeatWrapping;
|
||||
threeTexture.wrapT = THREE.RepeatWrapping;
|
||||
threeTexture.rotation = texture.rotation;
|
||||
threeTexture.offset.x = texture.offset.x;
|
||||
threeTexture.offset.y = texture.offset.y;
|
||||
threeTexture.repeat.x = texture.scale.x;
|
||||
threeTexture.repeat.y = texture.scale.y;
|
||||
}
|
||||
|
||||
function LoadTexture (threeMaterial, texture, onLoad)
|
||||
{
|
||||
if (texture === null || texture.url === null) {
|
||||
return;
|
||||
}
|
||||
let loader = new THREE.TextureLoader ();
|
||||
loader.load (texture.url, function (threeTexture) {
|
||||
SetTextureParameters (texture, threeTexture);
|
||||
threeTexture.image = OV.ResizeImageToPowerOfTwoSides (threeTexture.image);
|
||||
threeMaterial.needsUpdate = true;
|
||||
onLoad (threeTexture);
|
||||
});
|
||||
}
|
||||
|
||||
let material = model.GetMaterial (materialIndex);
|
||||
let diffuseColor = new THREE.Color (material.diffuse.r / 255.0, material.diffuse.g / 255.0, material.diffuse.b / 255.0);
|
||||
let specularColor = new THREE.Color (material.specular.r / 255.0, material.specular.g / 255.0, material.specular.b / 255.0);
|
||||
let emissiveColor = new THREE.Color (material.emissive.r / 255.0, material.emissive.g / 255.0, material.emissive.b / 255.0);
|
||||
if (OV.IsEqual (material.shininess, 0.0)) {
|
||||
specularColor.setRGB (0.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
let threeMaterial = new THREE.MeshPhongMaterial ({
|
||||
color : diffuseColor,
|
||||
specular : specularColor,
|
||||
emissive : emissiveColor,
|
||||
shininess : material.shininess * 10.0,
|
||||
opacity : material.opacity,
|
||||
transparent : material.transparent,
|
||||
alphaTest : material.alphaTest,
|
||||
side : THREE.DoubleSide
|
||||
});
|
||||
|
||||
LoadTexture (threeMaterial, material.diffuseMap, function (threeTexture) {
|
||||
if (!material.multiplyDiffuseMap) {
|
||||
threeMaterial.color.setRGB (1.0, 1.0, 1.0);
|
||||
}
|
||||
threeMaterial.map = threeTexture;
|
||||
callbacks.onTextureLoaded ();
|
||||
});
|
||||
LoadTexture (threeMaterial, material.specularMap, function (threeTexture) {
|
||||
threeMaterial.specularMap = threeTexture;
|
||||
callbacks.onTextureLoaded ();
|
||||
});
|
||||
LoadTexture (threeMaterial, material.bumpMap, function (threeTexture) {
|
||||
threeMaterial.bumpMap = threeTexture;
|
||||
callbacks.onTextureLoaded ();
|
||||
});
|
||||
LoadTexture (threeMaterial, material.normalMap, function (threeTexture) {
|
||||
threeMaterial.normalMap = threeTexture;
|
||||
callbacks.onTextureLoaded ();
|
||||
});
|
||||
LoadTexture (threeMaterial, material.emissiveMap, function (threeTexture) {
|
||||
threeMaterial.emissiveMap = threeTexture;
|
||||
callbacks.onTextureLoaded ();
|
||||
});
|
||||
|
||||
return threeMaterial;
|
||||
}
|
||||
|
||||
function CreateThreeMesh (model, meshIndex, modelThreeMaterials)
|
||||
{
|
||||
let mesh = model.GetMesh (meshIndex);
|
||||
let triangleCount = mesh.TriangleCount ();
|
||||
if (triangleCount === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let triangleIndices = [];
|
||||
for (let i = 0; i < triangleCount; i++) {
|
||||
triangleIndices.push (i);
|
||||
}
|
||||
triangleIndices.sort (function (a, b) {
|
||||
let aTriangle = mesh.GetTriangle (a);
|
||||
let bTriangle = mesh.GetTriangle (b);
|
||||
return aTriangle.mat - bTriangle.mat;
|
||||
});
|
||||
|
||||
let threeGeometry = new THREE.BufferGeometry ();
|
||||
let meshThreeMaterials = [];
|
||||
let meshOriginalMaterials = [];
|
||||
let modelToThreeMaterials = {};
|
||||
|
||||
let vertices = [];
|
||||
let normals = [];
|
||||
let uvs = [];
|
||||
|
||||
let groups = [];
|
||||
groups.push ({
|
||||
start : 0,
|
||||
end : -1
|
||||
});
|
||||
|
||||
let meshHasUVs = mesh.TextureUVCount () > 0;
|
||||
for (let i = 0; i < triangleIndices.length; i++) {
|
||||
let triangleIndex = triangleIndices[i];
|
||||
let triangle = mesh.GetTriangle (triangleIndex);
|
||||
|
||||
let v0 = mesh.GetVertex (triangle.v0);
|
||||
let v1 = mesh.GetVertex (triangle.v1);
|
||||
let v2 = mesh.GetVertex (triangle.v2);
|
||||
vertices.push (v0.x, v0.y, v0.z, v1.x, v1.y, v1.z, v2.x, v2.y, v2.z);
|
||||
|
||||
let n0 = mesh.GetNormal (triangle.n0);
|
||||
let n1 = mesh.GetNormal (triangle.n1);
|
||||
let n2 = mesh.GetNormal (triangle.n2);
|
||||
normals.push (n0.x, n0.y, n0.z, n1.x, n1.y, n1.z, n2.x, n2.y, n2.z);
|
||||
|
||||
if (triangle.HasTextureUVs ()) {
|
||||
let u0 = mesh.GetTextureUV (triangle.u0);
|
||||
let u1 = mesh.GetTextureUV (triangle.u1);
|
||||
let u2 = mesh.GetTextureUV (triangle.u2);
|
||||
uvs.push (u0.x, u0.y, u1.x, u1.y, u2.x, u2.y);
|
||||
} else if (meshHasUVs) {
|
||||
uvs.push (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
let modelMaterialIndex = triangle.mat;
|
||||
let materialIndex = modelToThreeMaterials[modelMaterialIndex];
|
||||
if (materialIndex === undefined) {
|
||||
materialIndex = meshThreeMaterials.length;
|
||||
modelToThreeMaterials[modelMaterialIndex] = materialIndex;
|
||||
meshThreeMaterials.push (modelThreeMaterials[modelMaterialIndex]);
|
||||
meshOriginalMaterials.push (modelMaterialIndex);
|
||||
if (i > 0) {
|
||||
groups[groups.length - 1].end = i - 1;
|
||||
groups.push ({
|
||||
start : groups[groups.length - 1].end + 1,
|
||||
end : -1
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
groups[groups.length - 1].end = triangleCount - 1;
|
||||
|
||||
threeGeometry.setAttribute ('position', new THREE.Float32BufferAttribute (vertices, 3));
|
||||
threeGeometry.setAttribute ('normal', new THREE.Float32BufferAttribute (normals, 3));
|
||||
if (uvs.length !== 0) {
|
||||
threeGeometry.setAttribute ('uv', new THREE.Float32BufferAttribute (uvs, 2));
|
||||
}
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
let group = groups[i];
|
||||
threeGeometry.addGroup (group.start * 3, (group.end - group.start + 1) * 3, i);
|
||||
}
|
||||
|
||||
let threeMesh = new THREE.Mesh (threeGeometry, meshThreeMaterials);
|
||||
threeMesh.userData = {
|
||||
originalMeshIndex : meshIndex,
|
||||
originalMaterials : meshOriginalMaterials,
|
||||
threeMaterials : null
|
||||
};
|
||||
|
||||
return threeMesh;
|
||||
}
|
||||
|
||||
let modelThreeMaterials = [];
|
||||
for (let materialIndex = 0; materialIndex < model.MaterialCount (); materialIndex++) {
|
||||
let threeMaterial = CreateThreeMaterial (model, materialIndex);
|
||||
modelThreeMaterials.push (threeMaterial);
|
||||
}
|
||||
|
||||
let threeMeshes = [];
|
||||
let taskRunner = new OV.TaskRunner ();
|
||||
taskRunner.Run (model.MeshCount (), {
|
||||
runTask : function (index, ready) {
|
||||
let mesh = model.GetMesh (index);
|
||||
if (mesh.TriangleCount () === 0) {
|
||||
ready ();
|
||||
} else {
|
||||
let threeMesh = CreateThreeMesh (model, index, modelThreeMaterials);
|
||||
threeMeshes.push (threeMesh);
|
||||
ready ();
|
||||
}
|
||||
},
|
||||
onReady : function () {
|
||||
callbacks.onModelLoaded (threeMeshes);
|
||||
}
|
||||
});
|
||||
};
|
||||
85
source/external/three.model.loader.js
vendored
Normal file
85
source/external/three.model.loader.js
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
OV.ThreeModelLoader = class
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
this.importer = new OV.Importer ();
|
||||
this.callbacks = null;
|
||||
this.inProgress = false;
|
||||
}
|
||||
|
||||
Init (callbacks)
|
||||
{
|
||||
this.callbacks = callbacks;
|
||||
}
|
||||
|
||||
LoadFromUrlList (urls)
|
||||
{
|
||||
if (this.inProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
let obj = this;
|
||||
this.inProgress = true;
|
||||
this.callbacks.onLoadStart ();
|
||||
this.importer.LoadFilesFromUrls (urls, function () {
|
||||
obj.OnFilesLoaded ();
|
||||
});
|
||||
}
|
||||
|
||||
LoadFromFileList (files)
|
||||
{
|
||||
if (this.inProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
let obj = this;
|
||||
this.inProgress = true;
|
||||
this.callbacks.onLoadStart ();
|
||||
this.importer.LoadFilesFromFileObjects (files, function () {
|
||||
obj.OnFilesLoaded ();
|
||||
});
|
||||
}
|
||||
|
||||
OnFilesLoaded ()
|
||||
{
|
||||
let obj = this;
|
||||
this.callbacks.onFilesLoaded ();
|
||||
|
||||
let taskRunner = new OV.TaskRunner ();
|
||||
taskRunner.Run (1, {
|
||||
runTask : function (index, ready) {
|
||||
obj.importer.Import ({
|
||||
success : function (importResult) {
|
||||
obj.OnModelImported (importResult);
|
||||
ready ();
|
||||
},
|
||||
error : function (importerError) {
|
||||
obj.callbacks.onLoadError (importerError);
|
||||
obj.inProgress = false;
|
||||
ready ();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
OnModelImported (importResult)
|
||||
{
|
||||
let obj = this;
|
||||
this.callbacks.onModelImported ();
|
||||
OV.ConvertModelToThreeMeshes (importResult.model, {
|
||||
onTextureLoaded : function () {
|
||||
obj.callbacks.onTextureLoaded ();
|
||||
},
|
||||
onModelLoaded : function (meshes) {
|
||||
obj.callbacks.onModelFinished (importResult, meshes);
|
||||
obj.inProgress = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
GetImporter ()
|
||||
{
|
||||
return this.importer;
|
||||
}
|
||||
};
|
||||
33
source/geometry/coord2d.js
Normal file
33
source/geometry/coord2d.js
Normal file
@ -0,0 +1,33 @@
|
||||
OV.Coord2D = class
|
||||
{
|
||||
constructor (x, y)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
Clone ()
|
||||
{
|
||||
return new OV.Coord2D (this.x, this.y);
|
||||
}
|
||||
};
|
||||
|
||||
OV.CoordIsEqual2D = function (a, b)
|
||||
{
|
||||
return OV.IsEqual (a.x, b.x) && OV.IsEqual (a.y, b.y);
|
||||
};
|
||||
|
||||
OV.AddCoord2D = function (a, b)
|
||||
{
|
||||
return new OV.Coord2D (a.x + b.x, a.y + b.y);
|
||||
};
|
||||
|
||||
OV.SubCoord2D = function (a, b)
|
||||
{
|
||||
return new OV.Coord2D (a.x - b.x, a.y - b.y);
|
||||
};
|
||||
|
||||
OV.CoordDistance2D = function (a, b)
|
||||
{
|
||||
return Math.sqrt ((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
|
||||
};
|
||||
119
source/geometry/coord3d.js
Normal file
119
source/geometry/coord3d.js
Normal file
@ -0,0 +1,119 @@
|
||||
OV.Coord3D = class
|
||||
{
|
||||
constructor (x, y, z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
Length ()
|
||||
{
|
||||
return Math.sqrt (this.x * this.x + this.y * this.y + this.z * this.z);
|
||||
}
|
||||
|
||||
MultiplyScalar (scalar)
|
||||
{
|
||||
this.x *= scalar;
|
||||
this.y *= scalar;
|
||||
this.z *= scalar;
|
||||
return this;
|
||||
}
|
||||
|
||||
Normalize ()
|
||||
{
|
||||
let length = this.Length ();
|
||||
if (length > 0.0) {
|
||||
this.MultiplyScalar (1.0 / length);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Offset (direction, distance)
|
||||
{
|
||||
let normal = direction.Clone ().Normalize ();
|
||||
this.x += normal.x * distance;
|
||||
this.y += normal.y * distance;
|
||||
this.z += normal.z * distance;
|
||||
return this;
|
||||
}
|
||||
|
||||
Rotate (axis, angle, origo)
|
||||
{
|
||||
let normal = axis.Clone ().Normalize ();
|
||||
|
||||
let u = normal.x;
|
||||
let v = normal.y;
|
||||
let w = normal.z;
|
||||
|
||||
let x = this.x - origo.x;
|
||||
let y = this.y - origo.y;
|
||||
let z = this.z - origo.z;
|
||||
|
||||
let si = Math.sin (angle);
|
||||
let co = Math.cos (angle);
|
||||
this.x = - u * (- u * x - v * y - w * z) * (1.0 - co) + x * co + (- w * y + v * z) * si;
|
||||
this.y = - v * (- u * x - v * y - w * z) * (1.0 - co) + y * co + (w * x - u * z) * si;
|
||||
this.z = - w * (- u * x - v * y - w * z) * (1.0 - co) + z * co + (- v * x + u * y) * si;
|
||||
|
||||
this.x += origo.x;
|
||||
this.y += origo.y;
|
||||
this.z += origo.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
Clone ()
|
||||
{
|
||||
return new OV.Coord3D (this.x, this.y, this.z);
|
||||
}
|
||||
};
|
||||
|
||||
OV.CoordIsEqual3D = function (a, b)
|
||||
{
|
||||
return OV.IsEqual (a.x, b.x) && OV.IsEqual (a.y, b.y) && OV.IsEqual (a.z, b.z);
|
||||
};
|
||||
|
||||
OV.AddCoord3D = function (a, b)
|
||||
{
|
||||
return new OV.Coord3D (a.x + b.x, a.y + b.y, a.z + b.z);
|
||||
};
|
||||
|
||||
OV.SubCoord3D = function (a, b)
|
||||
{
|
||||
return new OV.Coord3D (a.x - b.x, a.y - b.y, a.z - b.z);
|
||||
};
|
||||
|
||||
OV.CoordDistance3D = function (a, b)
|
||||
{
|
||||
return Math.sqrt ((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) + (a.z - b.z) * (a.z - b.z));
|
||||
};
|
||||
|
||||
OV.VectorAngle3D = function (a, b)
|
||||
{
|
||||
let aDirection = a.Clone ().Normalize ();
|
||||
let bDirection = b.Clone ().Normalize ();
|
||||
if (OV.CoordIsEqual3D (aDirection, bDirection)) {
|
||||
return 0.0;
|
||||
}
|
||||
let product = OV.DotVector3D (aDirection, bDirection);
|
||||
return Math.acos (product);
|
||||
};
|
||||
|
||||
OV.DotVector3D = function (a, b)
|
||||
{
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z;
|
||||
};
|
||||
|
||||
OV.CrossVector3D = function (a, b)
|
||||
{
|
||||
let result = new OV.Coord3D (0.0, 0.0, 0.0);
|
||||
result.x = a.y * b.z - a.z * b.y;
|
||||
result.y = a.z * b.x - a.x * b.z;
|
||||
result.z = a.x * b.y - a.y * b.x;
|
||||
return result;
|
||||
};
|
||||
|
||||
OV.VectorLength3D = function (x, y, z)
|
||||
{
|
||||
return Math.sqrt (x * x + y * y + z * z);
|
||||
};
|
||||
40
source/geometry/geometry.js
Normal file
40
source/geometry/geometry.js
Normal file
@ -0,0 +1,40 @@
|
||||
OV.Eps = 0.00000001;
|
||||
OV.RadDeg = 57.29577951308232;
|
||||
OV.DegRad = 0.017453292519943;
|
||||
|
||||
OV.IsZero = function (a)
|
||||
{
|
||||
return Math.abs (a) < OV.Eps;
|
||||
};
|
||||
|
||||
OV.IsLower = function (a, b)
|
||||
{
|
||||
return b - a > OV.Eps;
|
||||
};
|
||||
|
||||
OV.IsGreater = function (a, b)
|
||||
{
|
||||
return a - b > OV.Eps;
|
||||
};
|
||||
|
||||
OV.IsEqual = function (a, b)
|
||||
{
|
||||
return Math.abs (b - a) < OV.Eps;
|
||||
};
|
||||
|
||||
OV.IsPositive = function (a)
|
||||
{
|
||||
return a > OV.Eps;
|
||||
};
|
||||
|
||||
OV.IsNegative = function (a)
|
||||
{
|
||||
return a < -OV.Eps;
|
||||
};
|
||||
|
||||
OV.Direction =
|
||||
{
|
||||
X : 1,
|
||||
Y : 2,
|
||||
Z : 3
|
||||
};
|
||||
396
source/geometry/matrix.js
Normal file
396
source/geometry/matrix.js
Normal file
@ -0,0 +1,396 @@
|
||||
OV.Matrix = class
|
||||
{
|
||||
constructor (matrix)
|
||||
{
|
||||
this.matrix = null;
|
||||
if (matrix !== undefined && matrix !== null) {
|
||||
this.matrix = matrix;
|
||||
}
|
||||
}
|
||||
|
||||
IsValid ()
|
||||
{
|
||||
return this.matrix !== null;
|
||||
}
|
||||
|
||||
Set (matrix)
|
||||
{
|
||||
this.matrix = matrix;
|
||||
return this;
|
||||
}
|
||||
|
||||
Get ()
|
||||
{
|
||||
return this.matrix;
|
||||
}
|
||||
|
||||
Clone ()
|
||||
{
|
||||
let result = [
|
||||
this.matrix[0], this.matrix[1], this.matrix[2], this.matrix[3],
|
||||
this.matrix[4], this.matrix[5], this.matrix[6], this.matrix[7],
|
||||
this.matrix[8], this.matrix[9], this.matrix[10], this.matrix[11],
|
||||
this.matrix[12], this.matrix[13], this.matrix[14], this.matrix[15]
|
||||
];
|
||||
return new OV.Matrix (result);
|
||||
}
|
||||
|
||||
CreateIdentity ()
|
||||
{
|
||||
this.matrix = [
|
||||
1.0, 0.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0, 0.0,
|
||||
0.0, 0.0, 1.0, 0.0,
|
||||
0.0, 0.0, 0.0, 1.0
|
||||
];
|
||||
return this;
|
||||
}
|
||||
|
||||
IsIdentity ()
|
||||
{
|
||||
let identity = new OV.Matrix ().CreateIdentity ().Get ();
|
||||
for (let i = 0; i < 16; i++) {
|
||||
if (!OV.IsEqual (this.matrix[i], identity[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
CreateTranslation (x, y, z)
|
||||
{
|
||||
this.matrix = [
|
||||
1.0, 0.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0, 0.0,
|
||||
0.0, 0.0, 1.0, 0.0,
|
||||
x, y, z, 1.0
|
||||
];
|
||||
return this;
|
||||
}
|
||||
|
||||
CreateRotation (x, y, z, w)
|
||||
{
|
||||
let x2 = x + x;
|
||||
let y2 = y + y;
|
||||
let z2 = z + z;
|
||||
let xx = x * x2;
|
||||
let xy = x * y2;
|
||||
let xz = x * z2;
|
||||
let yy = y * y2;
|
||||
let yz = y * z2;
|
||||
let zz = z * z2;
|
||||
let wx = w * x2;
|
||||
let wy = w * y2;
|
||||
let wz = w * z2;
|
||||
this.matrix = [
|
||||
1.0 - (yy + zz), xy + wz, xz - wy, 0.0,
|
||||
xy - wz, 1.0 - (xx + zz), yz + wx, 0.0,
|
||||
xz + wy, yz - wx, 1.0 - (xx + yy), 0.0,
|
||||
0.0, 0.0, 0.0, 1.0
|
||||
];
|
||||
return this;
|
||||
}
|
||||
|
||||
CreateScale (x, y, z)
|
||||
{
|
||||
this.matrix = [
|
||||
x, 0.0, 0.0, 0.0,
|
||||
0.0, y, 0.0, 0.0,
|
||||
0.0, 0.0, z, 0.0,
|
||||
0.0, 0.0, 0.0, 1.0
|
||||
];
|
||||
return this;
|
||||
}
|
||||
|
||||
ComposeTRS (translation, rotation, scale)
|
||||
{
|
||||
let tx = translation[0];
|
||||
let ty = translation[1];
|
||||
let tz = translation[2];
|
||||
let qx = rotation[0];
|
||||
let qy = rotation[1];
|
||||
let qz = rotation[2];
|
||||
let qw = rotation[3];
|
||||
let sx = scale[0];
|
||||
let sy = scale[1];
|
||||
let sz = scale[2];
|
||||
|
||||
let x2 = qx + qx;
|
||||
let y2 = qy + qy;
|
||||
let z2 = qz + qz;
|
||||
let xx = qx * x2;
|
||||
let xy = qx * y2;
|
||||
let xz = qx * z2;
|
||||
let yy = qy * y2;
|
||||
let yz = qy * z2;
|
||||
let zz = qz * z2;
|
||||
let wx = qw * x2;
|
||||
let wy = qw * y2;
|
||||
let wz = qw * z2;
|
||||
|
||||
this.matrix = [
|
||||
(1.0 - (yy + zz)) * sx, (xy + wz) * sx, (xz - wy) * sx, 0.0,
|
||||
(xy - wz) * sy, (1.0 - (xx + zz)) * sy, (yz + wx) * sy, 0.0,
|
||||
(xz + wy) * sz, (yz - wx) * sz, (1.0 - (xx + yy)) * sz, 0.0,
|
||||
tx, ty, tz, 1.0
|
||||
];
|
||||
return this;
|
||||
}
|
||||
|
||||
DecomposeTRS ()
|
||||
{
|
||||
let translation = [
|
||||
this.matrix[12],
|
||||
this.matrix[13],
|
||||
this.matrix[14]
|
||||
];
|
||||
|
||||
let sx = OV.VectorLength3D (this.matrix[0], this.matrix[1], this.matrix[2]);
|
||||
let sy = OV.VectorLength3D (this.matrix[4], this.matrix[5], this.matrix[6]);
|
||||
let sz = OV.VectorLength3D (this.matrix[8], this.matrix[9], this.matrix[10]);
|
||||
let determinant = this.Determinant ();
|
||||
if (OV.IsNegative (determinant)) {
|
||||
sx *= -1.0;
|
||||
}
|
||||
let scale = [sx, sy, sz];
|
||||
|
||||
let m00 = this.matrix[0] / sx;
|
||||
let m01 = this.matrix[4] / sy;
|
||||
let m02 = this.matrix[8] / sz;
|
||||
let m10 = this.matrix[1] / sx;
|
||||
let m11 = this.matrix[5] / sy;
|
||||
let m12 = this.matrix[9] / sz;
|
||||
let m20 = this.matrix[2] / sx;
|
||||
let m21 = this.matrix[6] / sy;
|
||||
let m22 = this.matrix[10] / sz;
|
||||
|
||||
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
|
||||
let rotation = null;
|
||||
let tr = m00 + m11 + m22;
|
||||
if (tr > 0.0) {
|
||||
let s = Math.sqrt (tr + 1.0) * 2.0;
|
||||
rotation = [
|
||||
(m21 - m12) / s,
|
||||
(m02 - m20) / s,
|
||||
(m10 - m01) / s,
|
||||
0.25 * s
|
||||
];
|
||||
} else if ((m00 > m11) && (m00 > m22)) {
|
||||
let s = Math.sqrt (1.0 + m00 - m11 - m22) * 2.0;
|
||||
rotation = [
|
||||
0.25 * s,
|
||||
(m01 + m10) / s,
|
||||
(m02 + m20) / s,
|
||||
(m21 - m12) / s
|
||||
];
|
||||
} else if (m11 > m22) {
|
||||
let s = Math.sqrt (1.0 + m11 - m00 - m22) * 2.0;
|
||||
rotation = [
|
||||
(m01 + m10) / s,
|
||||
0.25 * s,
|
||||
(m12 + m21) / s,
|
||||
(m02 - m20) / s
|
||||
];
|
||||
} else {
|
||||
let s = Math.sqrt (1.0 + m22 - m00 - m11) * 2.0;
|
||||
rotation = [
|
||||
(m02 + m20) / s,
|
||||
(m12 + m21) / s,
|
||||
0.25 * s,
|
||||
(m10 - m01) / s
|
||||
];
|
||||
}
|
||||
|
||||
return {
|
||||
translation : translation,
|
||||
rotation : rotation,
|
||||
scale : scale
|
||||
};
|
||||
}
|
||||
|
||||
Determinant ()
|
||||
{
|
||||
let a00 = this.matrix[0];
|
||||
let a01 = this.matrix[1];
|
||||
let a02 = this.matrix[2];
|
||||
let a03 = this.matrix[3];
|
||||
let a10 = this.matrix[4];
|
||||
let a11 = this.matrix[5];
|
||||
let a12 = this.matrix[6];
|
||||
let a13 = this.matrix[7];
|
||||
let a20 = this.matrix[8];
|
||||
let a21 = this.matrix[9];
|
||||
let a22 = this.matrix[10];
|
||||
let a23 = this.matrix[11];
|
||||
let a30 = this.matrix[12];
|
||||
let a31 = this.matrix[13];
|
||||
let a32 = this.matrix[14];
|
||||
let a33 = this.matrix[15];
|
||||
|
||||
let b00 = a00 * a11 - a01 * a10;
|
||||
let b01 = a00 * a12 - a02 * a10;
|
||||
let b02 = a00 * a13 - a03 * a10;
|
||||
let b03 = a01 * a12 - a02 * a11;
|
||||
let b04 = a01 * a13 - a03 * a11;
|
||||
let b05 = a02 * a13 - a03 * a12;
|
||||
let b06 = a20 * a31 - a21 * a30;
|
||||
let b07 = a20 * a32 - a22 * a30;
|
||||
let b08 = a20 * a33 - a23 * a30;
|
||||
let b09 = a21 * a32 - a22 * a31;
|
||||
let b10 = a21 * a33 - a23 * a31;
|
||||
let b11 = a22 * a33 - a23 * a32;
|
||||
|
||||
let determinant = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
|
||||
return determinant;
|
||||
}
|
||||
|
||||
Invert ()
|
||||
{
|
||||
let a00 = this.matrix[0];
|
||||
let a01 = this.matrix[1];
|
||||
let a02 = this.matrix[2];
|
||||
let a03 = this.matrix[3];
|
||||
let a10 = this.matrix[4];
|
||||
let a11 = this.matrix[5];
|
||||
let a12 = this.matrix[6];
|
||||
let a13 = this.matrix[7];
|
||||
let a20 = this.matrix[8];
|
||||
let a21 = this.matrix[9];
|
||||
let a22 = this.matrix[10];
|
||||
let a23 = this.matrix[11];
|
||||
let a30 = this.matrix[12];
|
||||
let a31 = this.matrix[13];
|
||||
let a32 = this.matrix[14];
|
||||
let a33 = this.matrix[15];
|
||||
|
||||
let b00 = a00 * a11 - a01 * a10;
|
||||
let b01 = a00 * a12 - a02 * a10;
|
||||
let b02 = a00 * a13 - a03 * a10;
|
||||
let b03 = a01 * a12 - a02 * a11;
|
||||
let b04 = a01 * a13 - a03 * a11;
|
||||
let b05 = a02 * a13 - a03 * a12;
|
||||
let b06 = a20 * a31 - a21 * a30;
|
||||
let b07 = a20 * a32 - a22 * a30;
|
||||
let b08 = a20 * a33 - a23 * a30;
|
||||
let b09 = a21 * a32 - a22 * a31;
|
||||
let b10 = a21 * a33 - a23 * a31;
|
||||
let b11 = a22 * a33 - a23 * a32;
|
||||
|
||||
let determinant = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
|
||||
if (OV.IsEqual (determinant, 0.0)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let result = [
|
||||
(a11 * b11 - a12 * b10 + a13 * b09) / determinant,
|
||||
(a02 * b10 - a01 * b11 - a03 * b09) / determinant,
|
||||
(a31 * b05 - a32 * b04 + a33 * b03) / determinant,
|
||||
(a22 * b04 - a21 * b05 - a23 * b03) / determinant,
|
||||
(a12 * b08 - a10 * b11 - a13 * b07) / determinant,
|
||||
(a00 * b11 - a02 * b08 + a03 * b07) / determinant,
|
||||
(a32 * b02 - a30 * b05 - a33 * b01) / determinant,
|
||||
(a20 * b05 - a22 * b02 + a23 * b01) / determinant,
|
||||
(a10 * b10 - a11 * b08 + a13 * b06) / determinant,
|
||||
(a01 * b08 - a00 * b10 - a03 * b06) / determinant,
|
||||
(a30 * b04 - a31 * b02 + a33 * b00) / determinant,
|
||||
(a21 * b02 - a20 * b04 - a23 * b00) / determinant,
|
||||
(a11 * b07 - a10 * b09 - a12 * b06) / determinant,
|
||||
(a00 * b09 - a01 * b07 + a02 * b06) / determinant,
|
||||
(a31 * b01 - a30 * b03 - a32 * b00) / determinant,
|
||||
(a20 * b03 - a21 * b01 + a22 * b00) / determinant
|
||||
];
|
||||
|
||||
return new OV.Matrix (result);
|
||||
}
|
||||
|
||||
MultiplyVector (vector)
|
||||
{
|
||||
let a00 = vector[0];
|
||||
let a01 = vector[1];
|
||||
let a02 = vector[2];
|
||||
let a03 = vector[3];
|
||||
|
||||
let b00 = this.matrix[0];
|
||||
let b01 = this.matrix[1];
|
||||
let b02 = this.matrix[2];
|
||||
let b03 = this.matrix[3];
|
||||
let b10 = this.matrix[4];
|
||||
let b11 = this.matrix[5];
|
||||
let b12 = this.matrix[6];
|
||||
let b13 = this.matrix[7];
|
||||
let b20 = this.matrix[8];
|
||||
let b21 = this.matrix[9];
|
||||
let b22 = this.matrix[10];
|
||||
let b23 = this.matrix[11];
|
||||
let b30 = this.matrix[12];
|
||||
let b31 = this.matrix[13];
|
||||
let b32 = this.matrix[14];
|
||||
let b33 = this.matrix[15];
|
||||
|
||||
let result = [
|
||||
a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30,
|
||||
a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31,
|
||||
a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32,
|
||||
a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33
|
||||
];
|
||||
return result;
|
||||
}
|
||||
|
||||
MultiplyMatrix (matrix)
|
||||
{
|
||||
let a00 = this.matrix[0];
|
||||
let a01 = this.matrix[1];
|
||||
let a02 = this.matrix[2];
|
||||
let a03 = this.matrix[3];
|
||||
let a10 = this.matrix[4];
|
||||
let a11 = this.matrix[5];
|
||||
let a12 = this.matrix[6];
|
||||
let a13 = this.matrix[7];
|
||||
let a20 = this.matrix[8];
|
||||
let a21 = this.matrix[9];
|
||||
let a22 = this.matrix[10];
|
||||
let a23 = this.matrix[11];
|
||||
let a30 = this.matrix[12];
|
||||
let a31 = this.matrix[13];
|
||||
let a32 = this.matrix[14];
|
||||
let a33 = this.matrix[15];
|
||||
|
||||
let b00 = matrix.matrix[0];
|
||||
let b01 = matrix.matrix[1];
|
||||
let b02 = matrix.matrix[2];
|
||||
let b03 = matrix.matrix[3];
|
||||
let b10 = matrix.matrix[4];
|
||||
let b11 = matrix.matrix[5];
|
||||
let b12 = matrix.matrix[6];
|
||||
let b13 = matrix.matrix[7];
|
||||
let b20 = matrix.matrix[8];
|
||||
let b21 = matrix.matrix[9];
|
||||
let b22 = matrix.matrix[10];
|
||||
let b23 = matrix.matrix[11];
|
||||
let b30 = matrix.matrix[12];
|
||||
let b31 = matrix.matrix[13];
|
||||
let b32 = matrix.matrix[14];
|
||||
let b33 = matrix.matrix[15];
|
||||
|
||||
let result = [
|
||||
a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30,
|
||||
a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31,
|
||||
a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32,
|
||||
a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33,
|
||||
a10 * b00 + a11 * b10 + a12 * b20 + a13 * b30,
|
||||
a10 * b01 + a11 * b11 + a12 * b21 + a13 * b31,
|
||||
a10 * b02 + a11 * b12 + a12 * b22 + a13 * b32,
|
||||
a10 * b03 + a11 * b13 + a12 * b23 + a13 * b33,
|
||||
a20 * b00 + a21 * b10 + a22 * b20 + a23 * b30,
|
||||
a20 * b01 + a21 * b11 + a22 * b21 + a23 * b31,
|
||||
a20 * b02 + a21 * b12 + a22 * b22 + a23 * b32,
|
||||
a20 * b03 + a21 * b13 + a22 * b23 + a23 * b33,
|
||||
a30 * b00 + a31 * b10 + a32 * b20 + a33 * b30,
|
||||
a30 * b01 + a31 * b11 + a32 * b21 + a33 * b31,
|
||||
a30 * b02 + a31 * b12 + a32 * b22 + a33 * b32,
|
||||
a30 * b03 + a31 * b13 + a32 * b23 + a33 * b33
|
||||
];
|
||||
|
||||
return new OV.Matrix (result);
|
||||
}
|
||||
};
|
||||
41
source/geometry/transformation.js
Normal file
41
source/geometry/transformation.js
Normal file
@ -0,0 +1,41 @@
|
||||
OV.Transformation = class
|
||||
{
|
||||
constructor (matrix)
|
||||
{
|
||||
if (matrix !== undefined && matrix !== null) {
|
||||
this.matrix = matrix;
|
||||
} else {
|
||||
this.matrix = new OV.Matrix ();
|
||||
this.matrix.CreateIdentity ();
|
||||
}
|
||||
}
|
||||
|
||||
SetMatrix (matrix)
|
||||
{
|
||||
this.matrix = matrix;
|
||||
return this;
|
||||
}
|
||||
|
||||
GetMatrix ()
|
||||
{
|
||||
return this.matrix;
|
||||
}
|
||||
|
||||
IsIdentity ()
|
||||
{
|
||||
return this.matrix.IsIdentity ();
|
||||
}
|
||||
|
||||
AppendMatrix (matrix)
|
||||
{
|
||||
this.matrix = this.matrix.MultiplyMatrix (matrix);
|
||||
return this;
|
||||
}
|
||||
|
||||
TransformCoord3D (coord)
|
||||
{
|
||||
let resultVector = this.matrix.MultiplyVector ([coord.x, coord.y, coord.z, 1.0]);
|
||||
let result = new OV.Coord3D (resultVector[0], resultVector[1], resultVector[2]);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
29
source/geometry/tween.js
Normal file
29
source/geometry/tween.js
Normal file
@ -0,0 +1,29 @@
|
||||
OV.BezierTweenFunction = function (distance, index, count)
|
||||
{
|
||||
let t = index / count;
|
||||
return distance * (t * t * (3.0 - 2.0 * t));
|
||||
};
|
||||
|
||||
OV.LinearTweenFunction = function (distance, index, count)
|
||||
{
|
||||
return index * distance / count;
|
||||
};
|
||||
|
||||
OV.ParabolicTweenFunction = function (distance, index, count)
|
||||
{
|
||||
let t = index / count;
|
||||
let t2 = t * t;
|
||||
return distance * (t2 / (2.0 * (t2 - t) + 1.0));
|
||||
};
|
||||
|
||||
OV.TweenCoord3D = function (a, b, count, tweenFunc)
|
||||
{
|
||||
let dir = OV.SubCoord3D (b, a).Normalize ();
|
||||
let distance = OV.CoordDistance3D (a, b);
|
||||
let result = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
let step = tweenFunc (distance, i, count - 1);
|
||||
result.push (a.Clone ().Offset (dir, step));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
354
source/import/importer.js
Normal file
354
source/import/importer.js
Normal file
@ -0,0 +1,354 @@
|
||||
OV.File = class
|
||||
{
|
||||
constructor (file, source)
|
||||
{
|
||||
this.source = source;
|
||||
if (source === OV.FileSource.Url) {
|
||||
this.fileUrl = file;
|
||||
this.fileObject = null;
|
||||
this.name = OV.GetFileName (file);
|
||||
this.extension = OV.GetFileExtension (file);
|
||||
} else if (source === OV.FileSource.File) {
|
||||
this.fileUrl = null;
|
||||
this.fileObject = file;
|
||||
this.name = OV.GetFileName (file.name);
|
||||
this.extension = OV.GetFileExtension (file.name);
|
||||
}
|
||||
this.content = null;
|
||||
}
|
||||
};
|
||||
|
||||
OV.FileList = class
|
||||
{
|
||||
constructor (importers)
|
||||
{
|
||||
this.files = [];
|
||||
this.importers = importers;
|
||||
}
|
||||
|
||||
FillFromFileUrls (fileList)
|
||||
{
|
||||
this.Fill (fileList, OV.FileSource.Url);
|
||||
}
|
||||
|
||||
FillFromFileObjects (fileList)
|
||||
{
|
||||
this.Fill (fileList, OV.FileSource.File);
|
||||
}
|
||||
|
||||
ExtendFromFileList (files)
|
||||
{
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
let file = files[i];
|
||||
if (!this.ContainsFileByPath (file.name)) {
|
||||
this.files.push (file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GetFiles ()
|
||||
{
|
||||
return this.files;
|
||||
}
|
||||
|
||||
GetContent (onReady)
|
||||
{
|
||||
let obj = this;
|
||||
let taskRunner = new OV.TaskRunner ();
|
||||
taskRunner.Run (this.files.length, {
|
||||
runTask : function (index, complete) {
|
||||
obj.GetFileContent (obj.files[index], complete);
|
||||
},
|
||||
onReady : onReady
|
||||
});
|
||||
}
|
||||
|
||||
ContainsFileByPath (filePath)
|
||||
{
|
||||
return this.FindFileByPath (filePath) !== null;
|
||||
}
|
||||
|
||||
FindFileByPath (filePath)
|
||||
{
|
||||
let fileName = OV.GetFileName (filePath).toLowerCase ();
|
||||
for (let fileIndex = 0; fileIndex < this.files.length; fileIndex++) {
|
||||
let file = this.files[fileIndex];
|
||||
if (file.name.toLowerCase () === fileName) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
HasMainFile ()
|
||||
{
|
||||
return this.GetMainFile () !== null;
|
||||
}
|
||||
|
||||
GetMainFile ()
|
||||
{
|
||||
for (let fileIndex = 0; fileIndex < this.files.length; fileIndex++) {
|
||||
let file = this.files[fileIndex];
|
||||
let importer = this.FindImporter (file);
|
||||
if (importer !== null) {
|
||||
return {
|
||||
file : file,
|
||||
importer : importer
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
IsOnlySource (source)
|
||||
{
|
||||
if (this.files.length === 0) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < this.files.length; i++) {
|
||||
let file = this.files[i];
|
||||
if (file.source !== source) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Fill (fileList, fileSource)
|
||||
{
|
||||
this.files = [];
|
||||
for (let fileIndex = 0; fileIndex < fileList.length; fileIndex++) {
|
||||
let fileObject = fileList[fileIndex];
|
||||
let file = new OV.File (fileObject, fileSource);
|
||||
this.AddFile (file);
|
||||
}
|
||||
}
|
||||
|
||||
AddFile (file)
|
||||
{
|
||||
this.files.push (file);
|
||||
}
|
||||
|
||||
GetFileContent (file, complete)
|
||||
{
|
||||
let callbacks = {
|
||||
success : function (content) {
|
||||
file.content = content;
|
||||
},
|
||||
error : function () {
|
||||
|
||||
},
|
||||
complete : function () {
|
||||
complete ();
|
||||
}
|
||||
};
|
||||
if (file.content !== null) {
|
||||
complete ();
|
||||
return;
|
||||
}
|
||||
let format = this.GetFileFormat (file);
|
||||
if (file.source === OV.FileSource.Url) {
|
||||
OV.RequestUrl (file.fileUrl, format, callbacks);
|
||||
} else if (file.source === OV.FileSource.File) {
|
||||
OV.ReadFile (file.fileObject, format, callbacks);
|
||||
}
|
||||
}
|
||||
|
||||
GetFileFormat (file)
|
||||
{
|
||||
for (let importerIndex = 0; importerIndex < this.importers.length; importerIndex++) {
|
||||
let importer = this.importers[importerIndex];
|
||||
let extension = file.extension.toLowerCase ();
|
||||
let knownFormats = importer.GetKnownFileFormats ();
|
||||
if (knownFormats[extension] !== undefined) {
|
||||
return knownFormats[extension];
|
||||
}
|
||||
}
|
||||
return OV.FileFormat.Binary;
|
||||
}
|
||||
|
||||
FindImporter (file)
|
||||
{
|
||||
for (let importerIndex = 0; importerIndex < this.importers.length; importerIndex++) {
|
||||
let importer = this.importers[importerIndex];
|
||||
if (importer.CanImportExtension (file.extension.toLowerCase ())) {
|
||||
return importer;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
OV.ImporterErrorCode =
|
||||
{
|
||||
NoImportableFile : 1,
|
||||
ImportFailed : 2,
|
||||
UnknownError : 3
|
||||
};
|
||||
|
||||
OV.ImporterError = class
|
||||
{
|
||||
constructor (code, message)
|
||||
{
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
};
|
||||
|
||||
OV.ImportResult = class
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
this.model = null;
|
||||
this.mainFile = null;
|
||||
this.upVector = null;
|
||||
this.usedFiles = [];
|
||||
this.missingFiles = [];
|
||||
}
|
||||
};
|
||||
|
||||
OV.Importer = class
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
this.importers = [
|
||||
new OV.ImporterObj (),
|
||||
new OV.ImporterStl (),
|
||||
new OV.ImporterOff (),
|
||||
new OV.ImporterPly (),
|
||||
new OV.Importer3ds (),
|
||||
new OV.ImporterGltf ()
|
||||
];
|
||||
this.fileList = new OV.FileList (this.importers);
|
||||
this.missingFiles = [];
|
||||
}
|
||||
|
||||
LoadFilesFromUrls (fileList, onReady)
|
||||
{
|
||||
this.LoadFiles (fileList, OV.FileSource.Url, onReady);
|
||||
}
|
||||
|
||||
LoadFilesFromFileObjects (fileList, onReady)
|
||||
{
|
||||
this.LoadFiles (fileList, OV.FileSource.File, onReady);
|
||||
}
|
||||
|
||||
Import (callbacks)
|
||||
{
|
||||
let mainFile = this.fileList.GetMainFile ();
|
||||
if (mainFile === null || mainFile.file.content === null) {
|
||||
callbacks.error (new OV.ImporterError (OV.ImporterErrorCode.NoImportableFile, null));
|
||||
return;
|
||||
}
|
||||
|
||||
let result = new OV.ImportResult ();
|
||||
result.mainFile = mainFile.file.name;
|
||||
result.usedFiles.push (mainFile.file.name);
|
||||
|
||||
let fileNameToContent = {};
|
||||
let textureNameToUrl = {};
|
||||
|
||||
let obj = this;
|
||||
let importer = mainFile.importer;
|
||||
importer.Import (mainFile.file.content, mainFile.file.extension, {
|
||||
getDefaultMaterial : function () {
|
||||
let material = new OV.Material ();
|
||||
material.diffuse = new OV.Color (200, 200, 200);
|
||||
return material;
|
||||
},
|
||||
getFileContent : function (filePath) {
|
||||
let fileName = OV.GetFileName (filePath);
|
||||
let fileContent = fileNameToContent[fileName];
|
||||
if (fileContent === undefined) {
|
||||
let file = obj.fileList.FindFileByPath (filePath);
|
||||
if (file === null || file.content === null) {
|
||||
result.missingFiles.push (fileName);
|
||||
obj.missingFiles.push (fileName);
|
||||
fileContent = null;
|
||||
} else {
|
||||
result.usedFiles.push (fileName);
|
||||
fileContent = file.content;
|
||||
}
|
||||
fileNameToContent[fileName] = fileContent;
|
||||
}
|
||||
return fileContent;
|
||||
},
|
||||
getTextureObjectUrl : function (filePath) {
|
||||
let fileName = OV.GetFileName (filePath);
|
||||
let textureUrl = textureNameToUrl[fileName];
|
||||
if (textureUrl === undefined) {
|
||||
let file = obj.fileList.FindFileByPath (filePath);
|
||||
if (file === null || file.content === null) {
|
||||
result.missingFiles.push (fileName);
|
||||
obj.missingFiles.push (fileName);
|
||||
textureUrl = null;
|
||||
} else {
|
||||
result.usedFiles.push (fileName);
|
||||
let blob = new Blob ([file.content]);
|
||||
let blobURL = URL.createObjectURL (blob);
|
||||
textureUrl = blobURL;
|
||||
}
|
||||
textureNameToUrl[fileName] = textureUrl;
|
||||
}
|
||||
return textureUrl;
|
||||
}
|
||||
});
|
||||
|
||||
if (importer.IsError ()) {
|
||||
let message = importer.GetMessage ();
|
||||
callbacks.error (new OV.ImporterError (OV.ImporterErrorCode.ImportFailed, message));
|
||||
return;
|
||||
}
|
||||
|
||||
result.model = importer.GetModel ();
|
||||
result.model.SetName (mainFile.file.name);
|
||||
result.upVector = importer.GetUpDirection ();
|
||||
callbacks.success (result);
|
||||
}
|
||||
|
||||
LoadFiles (fileList, fileSource, onReady)
|
||||
{
|
||||
let newFileList = new OV.FileList (this.importers);
|
||||
if (fileSource === OV.FileSource.Url) {
|
||||
newFileList.FillFromFileUrls (fileList);
|
||||
} else if (fileSource === OV.FileSource.File) {
|
||||
newFileList.FillFromFileObjects (fileList);
|
||||
}
|
||||
let reset = false;
|
||||
if (newFileList.HasMainFile ()) {
|
||||
reset = true;
|
||||
} else {
|
||||
let foundMissingFile = false;
|
||||
for (let i = 0; i < this.missingFiles.length; i++) {
|
||||
let missingFile = this.missingFiles[i];
|
||||
if (newFileList.ContainsFileByPath (missingFile)) {
|
||||
foundMissingFile = true;
|
||||
}
|
||||
}
|
||||
if (!foundMissingFile) {
|
||||
reset = true;
|
||||
} else {
|
||||
let newFiles = newFileList.GetFiles ();
|
||||
this.fileList.ExtendFromFileList (newFiles);
|
||||
reset = false;
|
||||
}
|
||||
}
|
||||
if (reset) {
|
||||
this.fileList = newFileList;
|
||||
}
|
||||
this.missingFiles = [];
|
||||
this.fileList.GetContent (function () {
|
||||
onReady ();
|
||||
});
|
||||
}
|
||||
|
||||
GetFileList ()
|
||||
{
|
||||
return this.fileList;
|
||||
}
|
||||
|
||||
IsOnlyFileSource (source)
|
||||
{
|
||||
return this.fileList.IsOnlySource (source);
|
||||
}
|
||||
};
|
||||
679
source/import/importer3ds.js
Normal file
679
source/import/importer3ds.js
Normal file
@ -0,0 +1,679 @@
|
||||
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 textureObjectUrl = obj.callbacks.getTextureObjectUrl (textureName);
|
||||
texture.name = textureName;
|
||||
texture.url = textureObjectUrl;
|
||||
} 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 ();
|
||||
let flags = reader.ReadUnsignedInteger16 ();
|
||||
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);
|
||||
}
|
||||
};
|
||||
83
source/import/importerbase.js
Normal file
83
source/import/importerbase.js
Normal file
@ -0,0 +1,83 @@
|
||||
OV.ImporterBase = class
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
this.extension = null;
|
||||
this.callbacks = null;
|
||||
this.model = null;
|
||||
this.error = null;
|
||||
this.message = null;
|
||||
}
|
||||
|
||||
Import (content, extension, callbacks)
|
||||
{
|
||||
this.extension = extension;
|
||||
this.callbacks = callbacks;
|
||||
this.model = new OV.Model ();
|
||||
this.error = false;
|
||||
this.message = null;
|
||||
|
||||
this.ResetState ();
|
||||
this.ImportContent (content);
|
||||
if (this.error) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (OV.IsModelEmpty (this.model)) {
|
||||
this.error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
OV.FinalizeModel (this.model, this.callbacks.getDefaultMaterial);
|
||||
}
|
||||
|
||||
ResetState ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ImportContent (content)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CanImportExtension (extension)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
GetKnownFileFormats ()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
GetUpDirection ()
|
||||
{
|
||||
return OV.Direction.Z;
|
||||
}
|
||||
|
||||
GetModel ()
|
||||
{
|
||||
return this.model;
|
||||
}
|
||||
|
||||
SetError ()
|
||||
{
|
||||
this.error = true;
|
||||
}
|
||||
|
||||
IsError ()
|
||||
{
|
||||
return this.error;
|
||||
}
|
||||
|
||||
SetMessage (message)
|
||||
{
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
GetMessage ()
|
||||
{
|
||||
return this.message;
|
||||
}
|
||||
};
|
||||
899
source/import/importergltf.js
Normal file
899
source/import/importergltf.js
Normal file
@ -0,0 +1,899 @@
|
||||
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_materials_pbrSpecularGlossiness',
|
||||
'KHR_texture_transform',
|
||||
];
|
||||
}
|
||||
|
||||
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) {
|
||||
let diffuseColor = khrSpecularGlossiness.diffuseFactor;
|
||||
if (diffuseColor !== undefined) {
|
||||
material.diffuse = 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
OV.ImporterGltf = class extends OV.ImporterBase
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
super ();
|
||||
|
||||
this.bufferContents = null;
|
||||
this.gltfExtensions = null;
|
||||
}
|
||||
|
||||
ResetState ()
|
||||
{
|
||||
this.bufferContents = [];
|
||||
this.gltfExtensions = new OV.GltfExtensions ();
|
||||
}
|
||||
|
||||
CanImportExtension (extension)
|
||||
{
|
||||
return extension === 'gltf' || extension === 'glb';
|
||||
}
|
||||
|
||||
GetKnownFileFormats ()
|
||||
{
|
||||
return {
|
||||
'gltf' : OV.FileFormat.Text,
|
||||
'glb' : OV.FileFormat.Binary,
|
||||
'bin' : OV.FileFormat.Binary
|
||||
};
|
||||
}
|
||||
|
||||
GetUpDirection ()
|
||||
{
|
||||
return OV.Direction.Y;
|
||||
}
|
||||
|
||||
ImportContent (fileContent)
|
||||
{
|
||||
if (this.extension === 'gltf') {
|
||||
this.ProcessGltf (fileContent);
|
||||
} else if (this.extension === 'glb') {
|
||||
this.ProcessBinaryGltf (fileContent);
|
||||
}
|
||||
}
|
||||
|
||||
ProcessGltf (fileContent)
|
||||
{
|
||||
let gltf = JSON.parse (fileContent);
|
||||
if (gltf.asset.version !== '2.0') {
|
||||
this.SetError ();
|
||||
this.SetMessage ('Invalid glTF version.');
|
||||
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 = this.callbacks.getFileContent (gltfBuffer.uri);
|
||||
} else {
|
||||
buffer = base64Buffer.buffer;
|
||||
}
|
||||
if (buffer === null) {
|
||||
this.SetError ();
|
||||
this.SetMessage ('One of the requested buffers is missing.');
|
||||
return;
|
||||
}
|
||||
this.bufferContents.push (buffer);
|
||||
}
|
||||
|
||||
this.ProcessMainFile (gltf);
|
||||
}
|
||||
|
||||
ProcessBinaryGltf (fileContent)
|
||||
{
|
||||
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 ();
|
||||
this.SetMessage ('Invalid glTF file.');
|
||||
return;
|
||||
}
|
||||
let version = reader.ReadUnsignedInteger32 ();
|
||||
if (version !== 2) {
|
||||
this.SetError ();
|
||||
this.SetMessage ('Invalid glTF version.');
|
||||
return;
|
||||
}
|
||||
let length = reader.ReadUnsignedInteger32 ();
|
||||
if (length !== reader.GetByteLength ()) {
|
||||
this.SetError ();
|
||||
this.SetMessage ('Invalid glTF file.');
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
ProcessMainFile (gltf)
|
||||
{
|
||||
let unsupportedExtensions = this.gltfExtensions.GetUnsupportedExtensions (gltf.extensionsRequired);
|
||||
if (unsupportedExtensions.length > 0) {
|
||||
this.SetError ();
|
||||
this.SetMessage ('Unsupported extension: ' + unsupportedExtensions.join (', ') + '.');
|
||||
return;
|
||||
}
|
||||
|
||||
let defaultScene = this.GetDefaultScene (gltf);
|
||||
if (defaultScene === null) {
|
||||
this.SetError ();
|
||||
this.SetMessage ('No default scene found.');
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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, meshNodes)
|
||||
{
|
||||
let node = gltf.nodes[nodeIndex];
|
||||
if (node.mesh !== undefined) {
|
||||
nodeTree.AddMeshNode (nodeIndex);
|
||||
meshNodes.push (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, meshNodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let nodeTree = new OV.GltfNodeTree ();
|
||||
let meshNodes = [];
|
||||
for (let i = 0; i < scene.nodes.length; i++) {
|
||||
let nodeIndex = scene.nodes[i];
|
||||
CollectMeshNodeIndices (gltf, -1, nodeIndex, nodeTree, meshNodes);
|
||||
}
|
||||
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 ();
|
||||
if (gltfMaterial.name !== undefined) {
|
||||
material.name = gltfMaterial.name;
|
||||
}
|
||||
|
||||
material.diffuse = 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.diffuse = new OV.Color (
|
||||
GetMaterialComponent (baseColor[0]),
|
||||
GetMaterialComponent (baseColor[1]),
|
||||
GetMaterialComponent (baseColor[2])
|
||||
);
|
||||
material.opacity = baseColor[3];
|
||||
}
|
||||
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.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let obj = this;
|
||||
this.gltfExtensions.ProcessMaterial (gltfMaterial, material, function (textureRef) {
|
||||
return obj.ImportTexture (gltf, textureRef);
|
||||
});
|
||||
this.model.AddMaterial (material);
|
||||
}
|
||||
|
||||
ImportTexture (gltf, gltfTextureRef)
|
||||
{
|
||||
function GetTextureFileExtension (mimeType)
|
||||
{
|
||||
if (mimeType === undefined || mimeType === null) {
|
||||
return '';
|
||||
}
|
||||
let mimeParts = mimeType.split ('/');
|
||||
if (mimeParts.length === 0) {
|
||||
return '';
|
||||
}
|
||||
return '.' + mimeParts[mimeParts.length - 1];
|
||||
}
|
||||
|
||||
if (gltfTextureRef === undefined || gltfTextureRef === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let texture = new OV.TextureMap ();
|
||||
let gltfTexture = gltf.textures[gltfTextureRef.index];
|
||||
let gltfImage = gltf.images[gltfTexture.source];
|
||||
|
||||
let textureIndexString = gltfTexture.source.toString ();
|
||||
if (gltfImage.uri !== undefined) {
|
||||
let base64Buffer = OV.Base64DataURIToArrayBuffer (gltfImage.uri);
|
||||
if (base64Buffer !== null) {
|
||||
let blob = new Blob ([base64Buffer.buffer], { type : base64Buffer.mimeType });
|
||||
texture.name = 'Embedded_' + textureIndexString + GetTextureFileExtension (base64Buffer.mimeType);
|
||||
texture.url = URL.createObjectURL (blob);
|
||||
} else {
|
||||
let textureObjectUrl = this.callbacks.getTextureObjectUrl (gltfImage.uri);
|
||||
texture.name = gltfImage.uri;
|
||||
texture.url = textureObjectUrl;
|
||||
}
|
||||
} 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);
|
||||
let blob = new Blob ([buffer], { type: gltfImage.mimeType });
|
||||
texture.name = 'Binary_' + textureIndexString + GetTextureFileExtension (gltfImage.mimeType);
|
||||
texture.url = URL.createObjectURL (blob);
|
||||
}
|
||||
}
|
||||
|
||||
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 (translation, rotation, 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.AddMesh (mesh);
|
||||
if (gltfMesh.name !== undefined) {
|
||||
mesh.SetName (gltfMesh.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)
|
||||
{
|
||||
function AddTriangle (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);
|
||||
}
|
||||
|
||||
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);
|
||||
if (!hasVertices) {
|
||||
return;
|
||||
}
|
||||
|
||||
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 (function (data) {
|
||||
mesh.AddVertex (data);
|
||||
});
|
||||
}
|
||||
|
||||
if (hasNormals) {
|
||||
let accessor = gltf.accessors[primitive.attributes.NORMAL];
|
||||
let reader = this.GetReaderFromAccessor (gltf, accessor);
|
||||
if (reader === null) {
|
||||
return;
|
||||
}
|
||||
reader.EnumerateData (function (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 (function (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 (function (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];
|
||||
AddTriangle (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;
|
||||
}
|
||||
AddTriangle (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];
|
||||
AddTriangle (mesh, v0, v1, v2, hasNormals, hasUVs, vertexOffset, normalOffset, uvOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GetReaderFromBufferView (bufferView)
|
||||
{
|
||||
let bufferIndex = bufferView.buffer || 0;
|
||||
let buffer = this.bufferContents[bufferIndex];
|
||||
if (buffer === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let reader = new OV.GltfBufferReader (buffer);
|
||||
reader.SkipBytes (bufferView.byteOffset || 0);
|
||||
let byteStride = bufferView.byteStride;
|
||||
if (byteStride !== undefined) {
|
||||
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;
|
||||
}
|
||||
};
|
||||
385
source/import/importerobj.js
Normal file
385
source/import/importerobj.js
Normal file
@ -0,0 +1,385 @@
|
||||
OV.ImporterObj = class extends OV.ImporterBase
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
super ();
|
||||
|
||||
this.globalVertices = null;
|
||||
this.globalNormals = null;
|
||||
this.globalUvs = null;
|
||||
|
||||
this.currentMesh = null;
|
||||
this.currentMeshData = null;
|
||||
this.currentMaterial = null;
|
||||
this.currentMaterialIndex = null;
|
||||
|
||||
this.meshNameToMeshData = null;
|
||||
this.materialNameToIndex = null;
|
||||
}
|
||||
|
||||
ResetState ()
|
||||
{
|
||||
this.globalVertices = [];
|
||||
this.globalNormals = [];
|
||||
this.globalUvs = [];
|
||||
|
||||
this.currentMesh = null;
|
||||
this.currentMeshData = null;
|
||||
this.currentMaterial = null;
|
||||
this.currentMaterialIndex = null;
|
||||
|
||||
this.meshNameToMeshData = {};
|
||||
this.materialNameToIndex = {};
|
||||
}
|
||||
|
||||
CanImportExtension (extension)
|
||||
{
|
||||
return extension === 'obj';
|
||||
}
|
||||
|
||||
GetKnownFileFormats ()
|
||||
{
|
||||
return {
|
||||
'obj' : OV.FileFormat.Text,
|
||||
'mtl' : OV.FileFormat.Text
|
||||
};
|
||||
}
|
||||
|
||||
GetUpDirection ()
|
||||
{
|
||||
return OV.Direction.Y;
|
||||
}
|
||||
|
||||
ImportContent (fileContent)
|
||||
{
|
||||
let obj = this;
|
||||
OV.ReadLines (fileContent, function (line) {
|
||||
if (!obj.IsError ()) {
|
||||
obj.ProcessLine (line);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ProcessLine (line)
|
||||
{
|
||||
if (line[0] === '#') {
|
||||
return;
|
||||
}
|
||||
|
||||
let parameters = OV.ParametersFromLine (line, '#');
|
||||
if (parameters.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let keyword = parameters[0].toLowerCase ();
|
||||
parameters.shift ();
|
||||
|
||||
if (this.ProcessMeshParameter (keyword, parameters, line)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.ProcessMaterialParameter (keyword, parameters, line)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AddNewMesh (name)
|
||||
{
|
||||
let meshData = this.meshNameToMeshData[name];
|
||||
if (meshData === undefined) {
|
||||
let mesh = new OV.Mesh ();
|
||||
if (name !== null) {
|
||||
mesh.SetName (name);
|
||||
}
|
||||
this.model.AddMesh (mesh);
|
||||
meshData = {
|
||||
mesh : mesh,
|
||||
data : {
|
||||
globalToCurrentVertices : {},
|
||||
globalToCurrentNormals : {},
|
||||
globalToCurrentUvs : {}
|
||||
}
|
||||
};
|
||||
this.meshNameToMeshData[name] = meshData;
|
||||
}
|
||||
this.currentMesh = meshData.mesh;
|
||||
this.currentMeshData = meshData.data;
|
||||
}
|
||||
|
||||
ProcessMeshParameter (keyword, parameters, line)
|
||||
{
|
||||
if (keyword === 'g' || keyword === 'o') {
|
||||
if (parameters.length === 0) {
|
||||
return true;
|
||||
}
|
||||
let name = OV.NameFromLine (line, keyword.length, '#');
|
||||
this.AddNewMesh (name);
|
||||
return true;
|
||||
} else if (keyword === 'v') {
|
||||
if (parameters.length < 3) {
|
||||
return true;
|
||||
}
|
||||
this.globalVertices.push (new OV.Coord3D (
|
||||
parseFloat (parameters[0]),
|
||||
parseFloat (parameters[1]),
|
||||
parseFloat (parameters[2])
|
||||
));
|
||||
return true;
|
||||
} else if (keyword === 'vn') {
|
||||
if (parameters.length < 3) {
|
||||
return true;
|
||||
}
|
||||
this.globalNormals.push (new OV.Coord3D (
|
||||
parseFloat (parameters[0]),
|
||||
parseFloat (parameters[1]),
|
||||
parseFloat (parameters[2])
|
||||
));
|
||||
return true;
|
||||
} else if (keyword === 'vt') {
|
||||
if (parameters.length < 2) {
|
||||
return true;
|
||||
}
|
||||
this.globalUvs.push (new OV.Coord2D (
|
||||
parseFloat (parameters[0]),
|
||||
parseFloat (parameters[1])
|
||||
));
|
||||
return true;
|
||||
} else if (keyword === 'f') {
|
||||
if (parameters.length < 3) {
|
||||
return true;
|
||||
}
|
||||
this.ProcessFace (parameters);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ProcessMaterialParameter (keyword, parameters, line)
|
||||
{
|
||||
function CreateColor (parameters)
|
||||
{
|
||||
return new OV.Color (
|
||||
parseInt (parseFloat (parameters[0] * 255.0), 10),
|
||||
parseInt (parseFloat (parameters[1] * 255.0), 10),
|
||||
parseInt (parseFloat (parameters[2] * 255.0), 10)
|
||||
);
|
||||
}
|
||||
|
||||
function CreateTexture (keyword, line, callbacks)
|
||||
{
|
||||
let texture = new OV.TextureMap ();
|
||||
texture.name = OV.NameFromLine (line, keyword.length, '#');
|
||||
let textureObjectUrl = callbacks.getTextureObjectUrl (texture.name);
|
||||
if (textureObjectUrl !== null) {
|
||||
texture.url = textureObjectUrl;
|
||||
}
|
||||
return texture;
|
||||
}
|
||||
|
||||
let obj = this;
|
||||
if (keyword === 'newmtl') {
|
||||
if (parameters.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let material = new OV.Material ();
|
||||
let materialName = OV.NameFromLine (line, keyword.length, '#');
|
||||
let materialIndex = this.model.AddMaterial (material);
|
||||
material.name = materialName;
|
||||
this.currentMaterial = material;
|
||||
this.materialNameToIndex[materialName] = materialIndex;
|
||||
return true;
|
||||
} else if (keyword === 'usemtl') {
|
||||
if (parameters.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let materialName = OV.NameFromLine (line, keyword.length, '#');
|
||||
let materialIndex = this.materialNameToIndex[materialName];
|
||||
if (materialIndex !== undefined) {
|
||||
this.currentMaterialIndex = materialIndex;
|
||||
}
|
||||
return true;
|
||||
} else if (keyword === 'mtllib') {
|
||||
if (parameters.length === 0) {
|
||||
return true;
|
||||
}
|
||||
let fileName = OV.NameFromLine (line, keyword.length, '#');
|
||||
let fileContent = this.callbacks.getFileContent (fileName);
|
||||
if (fileContent !== null) {
|
||||
OV.ReadLines (fileContent, function (line) {
|
||||
if (!obj.IsError ()) {
|
||||
obj.ProcessLine (line);
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
} else if (keyword === 'map_kd') {
|
||||
if (this.currentMaterial === null || parameters.length === 0) {
|
||||
return true;
|
||||
}
|
||||
this.currentMaterial.diffuseMap = CreateTexture (keyword, line, this.callbacks);
|
||||
return true;
|
||||
} else if (keyword === 'map_ks') {
|
||||
if (this.currentMaterial === null || parameters.length === 0) {
|
||||
return true;
|
||||
}
|
||||
this.currentMaterial.specularMap = CreateTexture (keyword, line, this.callbacks);
|
||||
return true;
|
||||
} else if (keyword === 'map_bump' || keyword === 'bump') {
|
||||
if (this.currentMaterial === null || parameters.length === 0) {
|
||||
return true;
|
||||
}
|
||||
this.currentMaterial.bumpMap = CreateTexture (keyword, line, this.callbacks);
|
||||
return true;
|
||||
} else if (keyword === 'ka') {
|
||||
if (this.currentMaterial === null || parameters.length < 3) {
|
||||
return true;
|
||||
}
|
||||
this.currentMaterial.ambient = CreateColor (parameters);
|
||||
return true;
|
||||
} else if (keyword === 'kd') {
|
||||
if (this.currentMaterial === null || parameters.length < 3) {
|
||||
return true;
|
||||
}
|
||||
this.currentMaterial.diffuse = CreateColor (parameters);
|
||||
return true;
|
||||
} else if (keyword === 'ks') {
|
||||
if (this.currentMaterial === null || parameters.length < 3) {
|
||||
return true;
|
||||
}
|
||||
this.currentMaterial.specular = CreateColor (parameters);
|
||||
return true;
|
||||
} else if (keyword === 'ns') {
|
||||
if (this.currentMaterial === null || parameters.length < 1) {
|
||||
return true;
|
||||
}
|
||||
this.currentMaterial.shininess = parseFloat (parameters[0]) / 100.0;
|
||||
return true;
|
||||
} else if (keyword === 'tr') {
|
||||
if (this.currentMaterial === null || parameters.length < 1) {
|
||||
return true;
|
||||
}
|
||||
this.currentMaterial.opacity = 1.0 - parseFloat (parameters[0]);
|
||||
this.currentMaterial.transparent = OV.IsLower (this.currentMaterial.opacity, 1.0);
|
||||
return true;
|
||||
} else if (keyword === 'd') {
|
||||
if (this.currentMaterial === null || parameters.length < 1) {
|
||||
return true;
|
||||
}
|
||||
this.currentMaterial.opacity = parseFloat (parameters[0]);
|
||||
this.currentMaterial.transparent = OV.IsLower (this.currentMaterial.opacity, 1.0);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ProcessFace (parameters)
|
||||
{
|
||||
function GetRelativeIndex (index, count)
|
||||
{
|
||||
if (index > 0) {
|
||||
return index - 1;
|
||||
} else {
|
||||
return count + index;
|
||||
}
|
||||
}
|
||||
|
||||
function GetLocalIndex (globalValueArray, globalToCurrentIndices, globalIndex, valueAdderFunc)
|
||||
{
|
||||
if (isNaN (globalIndex) || globalIndex < 0 || globalIndex >= globalValueArray.length) {
|
||||
return null;
|
||||
}
|
||||
let result = globalToCurrentIndices[globalIndex];
|
||||
if (result === undefined) {
|
||||
let globalValue = globalValueArray[globalIndex];
|
||||
if (globalValue === undefined) {
|
||||
return null;
|
||||
}
|
||||
result = valueAdderFunc (globalValue);
|
||||
globalToCurrentIndices[globalIndex] = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function GetLocalVertexIndex (obj, mesh, globalIndex)
|
||||
{
|
||||
return GetLocalIndex (obj.globalVertices, obj.currentMeshData.globalToCurrentVertices, globalIndex, function (val) {
|
||||
return mesh.AddVertex (new OV.Coord3D (val.x, val.y, val.z));
|
||||
});
|
||||
}
|
||||
|
||||
function GetLocalNormalIndex (obj, mesh, globalIndex)
|
||||
{
|
||||
return GetLocalIndex (obj.globalNormals, obj.currentMeshData.globalToCurrentNormals, globalIndex, function (val) {
|
||||
return mesh.AddNormal (new OV.Coord3D (val.x, val.y, val.z));
|
||||
});
|
||||
}
|
||||
|
||||
function GetLocalUVIndex (obj, mesh, globalIndex)
|
||||
{
|
||||
return GetLocalIndex (obj.globalUvs, obj.currentMeshData.globalToCurrentUvs, globalIndex, function (val) {
|
||||
return mesh.AddTextureUV (new OV.Coord2D (val.x, val.y));
|
||||
});
|
||||
}
|
||||
|
||||
let vertices = [];
|
||||
let normals = [];
|
||||
let uvs = [];
|
||||
|
||||
for (let i = 0; i < parameters.length; i++) {
|
||||
let vertexParams = parameters[i].split ('/');
|
||||
vertices.push (GetRelativeIndex (parseInt (vertexParams[0], 10), this.globalVertices.length));
|
||||
if (vertexParams.length > 1 && vertexParams[1].length > 0) {
|
||||
uvs.push (GetRelativeIndex (parseInt (vertexParams[1], 10), this.globalUvs.length));
|
||||
}
|
||||
if (vertexParams.length > 2 && vertexParams[2].length > 0) {
|
||||
normals.push (GetRelativeIndex (parseInt (vertexParams[2], 10), this.globalNormals.length));
|
||||
}
|
||||
}
|
||||
|
||||
if (this.currentMesh === null) {
|
||||
this.AddNewMesh ('');
|
||||
}
|
||||
|
||||
for (let i = 0; i < vertices.length - 2; i++) {
|
||||
let v0 = GetLocalVertexIndex (this, this.currentMesh, vertices[0]);
|
||||
let v1 = GetLocalVertexIndex (this, this.currentMesh, vertices[i + 1]);
|
||||
let v2 = GetLocalVertexIndex (this, this.currentMesh, vertices[i + 2]);
|
||||
if (v0 === null || v1 === null || v2 === null) {
|
||||
this.SetError ();
|
||||
this.SetMessage ('Invalid vertex index.');
|
||||
break;
|
||||
}
|
||||
let triangle = new OV.Triangle (v0, v1, v2);
|
||||
if (normals.length === vertices.length) {
|
||||
let n0 = GetLocalNormalIndex (this, this.currentMesh, normals[0]);
|
||||
let n1 = GetLocalNormalIndex (this, this.currentMesh, normals[i + 1]);
|
||||
let n2 = GetLocalNormalIndex (this, this.currentMesh, normals[i + 2]);
|
||||
if (n0 === null || n1 === null || n2 === null) {
|
||||
this.SetError ();
|
||||
this.SetMessage ('Invalid normal index.');
|
||||
break;
|
||||
}
|
||||
triangle.SetNormals (n0, n1, n2);
|
||||
}
|
||||
if (uvs.length === vertices.length) {
|
||||
let u0 = GetLocalUVIndex (this, this.currentMesh, uvs[0]);
|
||||
let u1 = GetLocalUVIndex (this, this.currentMesh, uvs[i + 1]);
|
||||
let u2 = GetLocalUVIndex (this, this.currentMesh, uvs[i + 2]);
|
||||
if (u0 === null || u1 === null || u2 === null) {
|
||||
this.SetError ();
|
||||
this.SetMessage ('Invalid uv index.');
|
||||
break;
|
||||
}
|
||||
triangle.SetTextureUVs (u0, u1, u2);
|
||||
}
|
||||
if (this.currentMaterialIndex !== null) {
|
||||
triangle.mat = this.currentMaterialIndex;
|
||||
}
|
||||
this.currentMesh.AddTriangle (triangle);
|
||||
}
|
||||
}
|
||||
};
|
||||
103
source/import/importeroff.js
Normal file
103
source/import/importeroff.js
Normal file
@ -0,0 +1,103 @@
|
||||
OV.ImporterOff = class extends OV.ImporterBase
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
super ();
|
||||
this.model = null;
|
||||
this.mesh = null;
|
||||
this.status = null;
|
||||
}
|
||||
|
||||
ResetState ()
|
||||
{
|
||||
this.mesh = new OV.Mesh ();
|
||||
this.model.AddMesh (this.mesh);
|
||||
this.status = {
|
||||
vertexCount : 0,
|
||||
faceCount : 0,
|
||||
foundVertex : 0,
|
||||
foundFace : 0
|
||||
};
|
||||
}
|
||||
|
||||
CanImportExtension (extension)
|
||||
{
|
||||
return extension === 'off';
|
||||
}
|
||||
|
||||
GetKnownFileFormats ()
|
||||
{
|
||||
return {
|
||||
'off' : OV.FileFormat.Text
|
||||
};
|
||||
}
|
||||
|
||||
GetUpDirection ()
|
||||
{
|
||||
return OV.Direction.Y;
|
||||
}
|
||||
|
||||
ImportContent (fileContent)
|
||||
{
|
||||
let obj = this;
|
||||
OV.ReadLines (fileContent, function (line) {
|
||||
if (!obj.IsError ()) {
|
||||
obj.ProcessLine (line);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ProcessLine (line)
|
||||
{
|
||||
if (line[0] === '#') {
|
||||
return;
|
||||
}
|
||||
|
||||
let parameters = OV.ParametersFromLine (line, '#');
|
||||
if (parameters.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (parameters[0] === 'OFF') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.status.vertexCount === 0 && this.status.faceCount === 0) {
|
||||
if (parameters.length > 1) {
|
||||
this.status.vertexCount = parseInt (parameters[0], 10);
|
||||
this.status.faceCount = parseInt (parameters[1], 10);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.status.foundVertex < this.status.vertexCount) {
|
||||
if (parameters.length >= 3) {
|
||||
this.mesh.AddVertex (new OV.Coord3D (
|
||||
parseFloat (parameters[0]),
|
||||
parseFloat (parameters[1]),
|
||||
parseFloat (parameters[2])
|
||||
));
|
||||
this.status.foundVertex += 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.status.foundFace < this.status.faceCount) {
|
||||
if (parameters.length >= 4) {
|
||||
let vertexCount = parseInt (parameters[0], 10);
|
||||
if (parameters.length < vertexCount + 1) {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < vertexCount - 2; i++) {
|
||||
let v0 = parseInt (parameters[1]);
|
||||
let v1 = parseInt (parameters[i + 2]);
|
||||
let v2 = parseInt (parameters[i + 3]);
|
||||
let triangle = new OV.Triangle (v0, v1, v2);
|
||||
this.mesh.AddTriangle (triangle);
|
||||
}
|
||||
this.status.foundFace += 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
428
source/import/importerply.js
Normal file
428
source/import/importerply.js
Normal file
@ -0,0 +1,428 @@
|
||||
OV.PlyHeader = class
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
this.format = null;
|
||||
this.elements = [];
|
||||
}
|
||||
|
||||
SetFormat (format)
|
||||
{
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
AddElement (name, count)
|
||||
{
|
||||
this.elements.push ({
|
||||
name : name,
|
||||
count : count,
|
||||
format : []
|
||||
});
|
||||
}
|
||||
|
||||
GetElements ()
|
||||
{
|
||||
return this.elements;
|
||||
}
|
||||
|
||||
AddSingleFormat (elemType, name)
|
||||
{
|
||||
let lastElement = this.elements[this.elements.length - 1];
|
||||
lastElement.format.push ({
|
||||
name : name,
|
||||
isSingle : true,
|
||||
elemType : elemType
|
||||
});
|
||||
}
|
||||
|
||||
AddListFormat (countType, elemType, name)
|
||||
{
|
||||
let lastElement = this.elements[this.elements.length - 1];
|
||||
lastElement.format.push ({
|
||||
name : name,
|
||||
isSingle : false,
|
||||
countType : countType,
|
||||
elemType : elemType
|
||||
});
|
||||
}
|
||||
|
||||
GetElement (name)
|
||||
{
|
||||
for (let i = 0; i < this.elements.length; i++) {
|
||||
let element = this.elements[i];
|
||||
if (element.name === name) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Check ()
|
||||
{
|
||||
let vertex = this.GetElement ('vertex');
|
||||
if (vertex === null || vertex.length === 0 || vertex.format.length < 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let face = this.GetElement ('face');
|
||||
if (this.format === 'ascii') {
|
||||
if (face === null || face.count === 0 || face.format.length < 0) {
|
||||
return false;
|
||||
}
|
||||
} else if (this.format === 'binary_little_endian' || this.format === 'binary_big_endian') {
|
||||
let triStrips = this.GetElement ('tristrips');
|
||||
let hasFaces = (face !== null && face.count > 0 && face.format.length > 0);
|
||||
let hasTriStrips = (triStrips !== null && triStrips.count > 0 && triStrips.format.length > 0);
|
||||
if (!hasFaces && !hasTriStrips) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
OV.ImporterPly = class extends OV.ImporterBase
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
super ();
|
||||
this.model = null;
|
||||
this.mesh = null;
|
||||
}
|
||||
|
||||
ResetState ()
|
||||
{
|
||||
this.mesh = new OV.Mesh ();
|
||||
this.model.AddMesh (this.mesh);
|
||||
}
|
||||
|
||||
CanImportExtension (extension)
|
||||
{
|
||||
return extension === 'ply';
|
||||
}
|
||||
|
||||
GetKnownFileFormats ()
|
||||
{
|
||||
return {
|
||||
'ply' : OV.FileFormat.Binary
|
||||
};
|
||||
}
|
||||
|
||||
GetUpDirection ()
|
||||
{
|
||||
return OV.Direction.Y;
|
||||
}
|
||||
|
||||
ImportContent (fileContent)
|
||||
{
|
||||
let headerString = this.GetHeaderContent (fileContent);
|
||||
let header = this.ReadHeader (headerString);
|
||||
if (!header.Check ()) {
|
||||
this.SetError ();
|
||||
this.SetMessage ('Invalid header information.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (header.format === 'ascii') {
|
||||
let contentString = OV.ArrayBufferToUtf8String (fileContent);
|
||||
contentString = contentString.substr (headerString.length);
|
||||
this.ReadAsciiContent (header, contentString);
|
||||
} else if (header.format === 'binary_little_endian' || header.format === 'binary_big_endian') {
|
||||
this.ReadBinaryContent (header, fileContent, headerString.length);
|
||||
}
|
||||
}
|
||||
|
||||
GetHeaderContent (fileContent)
|
||||
{
|
||||
let headerContent = '';
|
||||
let bufferView = new Uint8Array (fileContent);
|
||||
let bufferIndex = 0;
|
||||
for (bufferIndex = 0; bufferIndex < fileContent.byteLength; bufferIndex++) {
|
||||
headerContent += String.fromCharCode (bufferView[bufferIndex]);
|
||||
if (headerContent.endsWith ('end_header')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
bufferIndex += 1;
|
||||
while (bufferIndex < fileContent.byteLength) {
|
||||
let char = String.fromCharCode (bufferView[bufferIndex]);
|
||||
headerContent += char;
|
||||
bufferIndex += 1;
|
||||
if (char === '\n') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return headerContent;
|
||||
}
|
||||
|
||||
ReadHeader (headerContent)
|
||||
{
|
||||
let header = new OV.PlyHeader ();
|
||||
OV.ReadLines (headerContent, function (line) {
|
||||
let parameters = OV.ParametersFromLine (line, null);
|
||||
if (parameters.length === 0 || parameters[0] === 'comment') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (parameters[0] === 'ply') {
|
||||
return;
|
||||
} else if (parameters[0] === 'format' && parameters.length >= 2) {
|
||||
header.SetFormat (parameters[1]);
|
||||
} else if (parameters[0] === 'element' && parameters.length >= 3) {
|
||||
header.AddElement (parameters[1], parseInt (parameters[2], 10));
|
||||
} else if (parameters[0] === 'property' && parameters.length >= 3) {
|
||||
if (parameters[1] === 'list' && parameters.length >= 5) {
|
||||
header.AddListFormat (parameters[2], parameters[3], parameters[4]);
|
||||
} else {
|
||||
header.AddSingleFormat (parameters[1], parameters[2]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
ReadAsciiContent (header, fileContent)
|
||||
{
|
||||
let obj = this;
|
||||
let vertex = header.GetElement ('vertex');
|
||||
let face = header.GetElement ('face');
|
||||
let foundVertex = 0;
|
||||
let foundFace = 0;
|
||||
OV.ReadLines (fileContent, function (line) {
|
||||
if (obj.IsError ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let parameters = OV.ParametersFromLine (line, null);
|
||||
if (parameters.length === 0 || parameters[0] === 'comment') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (foundVertex < vertex.count) {
|
||||
if (parameters.length >= 3) {
|
||||
obj.mesh.AddVertex (new OV.Coord3D (
|
||||
parseFloat (parameters[0]),
|
||||
parseFloat (parameters[1]),
|
||||
parseFloat (parameters[2])
|
||||
));
|
||||
foundVertex += 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (foundFace < face.count) {
|
||||
if (parameters.length >= 4) {
|
||||
let vertexCount = parseInt (parameters[0], 10);
|
||||
if (parameters.length < vertexCount + 1) {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < vertexCount - 2; i++) {
|
||||
let v0 = parseInt (parameters[1]);
|
||||
let v1 = parseInt (parameters[i + 2]);
|
||||
let v2 = parseInt (parameters[i + 3]);
|
||||
let triangle = new OV.Triangle (v0, v1, v2);
|
||||
obj.mesh.AddTriangle (triangle);
|
||||
}
|
||||
foundFace += 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ReadBinaryContent (header, fileContent, headerLength)
|
||||
{
|
||||
function SkipAndGetColor (obj, reader, format, startIndex)
|
||||
{
|
||||
let r = null;
|
||||
let g = null;
|
||||
let b = null;
|
||||
let a = 255;
|
||||
|
||||
for (let i = startIndex; i < format.length; i++) {
|
||||
let currFormat = format[i];
|
||||
let val = obj.ReadByFormat (reader, currFormat);
|
||||
if (currFormat.name === 'red') {
|
||||
r = val;
|
||||
} else if (currFormat.name === 'green') {
|
||||
g = val;
|
||||
} else if (currFormat.name === 'blue') {
|
||||
b = val;
|
||||
} else if (currFormat.name === 'alpha') {
|
||||
a = val;
|
||||
}
|
||||
}
|
||||
|
||||
if (r !== null && g !== null && b !== null) {
|
||||
return [r, g, b, a];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function GetMaterialFromColor (obj, color, colorToMaterial)
|
||||
{
|
||||
function IntegerToHex (intVal)
|
||||
{
|
||||
let result = parseInt (intVal, 10).toString (16);
|
||||
while (result.length < 2) {
|
||||
result = '0' + result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (color === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let materialName = '#' + IntegerToHex (color[0]) + IntegerToHex (color[1]) + IntegerToHex (color[2]) + IntegerToHex (color[3]);
|
||||
let materialIndex = colorToMaterial[materialName];
|
||||
if (materialIndex === undefined) {
|
||||
let material = new OV.Material ();
|
||||
material.name = materialName;
|
||||
material.diffuse = new OV.Color (color[0], color[1], color[2]);
|
||||
material.opacity = color[3] / 255.0;
|
||||
material.transparent = OV.IsLower (material.opacity, 1.0);
|
||||
materialIndex = obj.model.AddMaterial (material);
|
||||
colorToMaterial[materialName] = materialIndex;
|
||||
}
|
||||
|
||||
return materialIndex;
|
||||
}
|
||||
|
||||
function GetFaceColorByVertexColors (obj, v0, v1, v2, vertexColors, colorToMaterial)
|
||||
{
|
||||
let c0 = vertexColors[v0];
|
||||
let c1 = vertexColors[v1];
|
||||
let c2 = vertexColors[v2];
|
||||
let d0 = Math.abs (c0[0] - c1[0]) + Math.abs (c0[1] - c1[1]) + Math.abs (c0[2] - c1[2]);
|
||||
let d1 = Math.abs (c1[0] - c2[0]) + Math.abs (c1[1] - c2[1]) + Math.abs (c1[2] - c2[2]);
|
||||
let d2 = Math.abs (c0[0] - c2[0]) + Math.abs (c0[1] - c2[1]) + Math.abs (c0[2] - c2[2]);
|
||||
let min = Math.min (d0, d1, d2);
|
||||
let color = c0;
|
||||
if (min === d1) {
|
||||
color = c1;
|
||||
}
|
||||
return GetMaterialFromColor (obj, color, colorToMaterial);
|
||||
}
|
||||
|
||||
let reader = null;
|
||||
if (header.format === 'binary_little_endian') {
|
||||
reader = new OV.BinaryReader (fileContent, true);
|
||||
} else if (header.format === 'binary_big_endian') {
|
||||
reader = new OV.BinaryReader (fileContent, false);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
reader.Skip (headerLength);
|
||||
|
||||
let vertexColors = [];
|
||||
let colorToMaterial = {};
|
||||
let elements = header.GetElements ();
|
||||
for (let elementIndex = 0; elementIndex < elements.length; elementIndex++) {
|
||||
let element = elements[elementIndex];
|
||||
if (element.name === 'vertex') {
|
||||
for (let vertexIndex = 0; vertexIndex < element.count; vertexIndex++) {
|
||||
let x = this.ReadByFormat (reader, element.format[0]);
|
||||
let y = this.ReadByFormat (reader, element.format[1]);
|
||||
let z = this.ReadByFormat (reader, element.format[2]);
|
||||
let color = SkipAndGetColor (this, reader, element.format, 3);
|
||||
if (color !== null) {
|
||||
vertexColors.push (color);
|
||||
}
|
||||
this.mesh.AddVertex (new OV.Coord3D (x, y, z));
|
||||
}
|
||||
} else if (element.name === 'face') {
|
||||
for (let faceIndex = 0; faceIndex < element.count; faceIndex++) {
|
||||
let vertices = this.ReadByFormat (reader, element.format[0]);
|
||||
let faceColor = SkipAndGetColor (this, reader, element.format, 1);
|
||||
for (let i = 0; i < vertices.length - 2; i++) {
|
||||
let v0 = vertices[0];
|
||||
let v1 = vertices[i + 1];
|
||||
let v2 = vertices[i + 2];
|
||||
let triangle = new OV.Triangle (v0, v1, v2);
|
||||
if (faceColor !== null) {
|
||||
triangle.mat = GetMaterialFromColor (this, faceColor, colorToMaterial);
|
||||
} else if (vertexColors.length === this.model.VertexCount ()) {
|
||||
triangle.mat = GetFaceColorByVertexColors (this, v0, v1, v2, vertexColors, colorToMaterial);
|
||||
}
|
||||
this.mesh.AddTriangle (triangle);
|
||||
}
|
||||
}
|
||||
} else if (element.name === 'tristrips') {
|
||||
for (let triStripIndex = 0; triStripIndex < element.count; triStripIndex++) {
|
||||
let vertices = this.ReadByFormat (reader, element.format[0]);
|
||||
this.SkipFormat (reader, element.format, 1);
|
||||
let ccw = true;
|
||||
for (let i = 0; i < vertices.length - 2; i++) {
|
||||
let v0 = vertices[i];
|
||||
let v1 = vertices[i + 1];
|
||||
let v2 = vertices[i + 2];
|
||||
if (v2 === -1) {
|
||||
i += 2;
|
||||
ccw = true;
|
||||
continue;
|
||||
}
|
||||
if (!ccw) {
|
||||
let tmp = v1;
|
||||
v1 = v2;
|
||||
v2 = tmp;
|
||||
}
|
||||
ccw = !ccw;
|
||||
let triangle = new OV.Triangle (v0, v1, v2);
|
||||
this.mesh.AddTriangle (triangle);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.SkipFormat (reader, element.format, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SkipFormat (reader, format, startIndex)
|
||||
{
|
||||
for (let i = startIndex; i < format.length; i++) {
|
||||
this.ReadByFormat (reader, format[i]);
|
||||
}
|
||||
}
|
||||
|
||||
ReadByFormat (reader, format)
|
||||
{
|
||||
function ReadType (reader, type)
|
||||
{
|
||||
if (type === 'char' || type === 'int8') {
|
||||
return reader.ReadCharacter8 ();
|
||||
} else if (type === 'uchar' || type === 'uint8') {
|
||||
return reader.ReadUnsignedCharacter8 ();
|
||||
} else if (type === 'short' || type === 'int16') {
|
||||
return reader.ReadInteger16 ();
|
||||
} else if (type === 'ushort' || type === 'uint16') {
|
||||
return reader.ReadUnsignedInteger16 ();
|
||||
} else if (type === 'int' || type === 'int32') {
|
||||
return reader.ReadInteger32 ();
|
||||
} else if (type === 'uint' || type === 'uint32') {
|
||||
return reader.ReadUnsignedInteger32 ();
|
||||
} else if (type === 'float' || type === 'float32') {
|
||||
return reader.ReadFloat32 ();
|
||||
} else if (type === 'double' || type === 'double64') {
|
||||
return reader.ReadDouble64 ();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (format.isSingle) {
|
||||
return ReadType (reader, format.elemType);
|
||||
} else {
|
||||
let list = [];
|
||||
let count = ReadType (reader, format.countType);
|
||||
for (let i = 0; i < count; i++) {
|
||||
list.push (ReadType (reader, format.elemType));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
};
|
||||
168
source/import/importerstl.js
Normal file
168
source/import/importerstl.js
Normal file
@ -0,0 +1,168 @@
|
||||
OV.ImporterStl = class extends OV.ImporterBase
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
super ();
|
||||
this.mesh = null;
|
||||
this.triangle = null;
|
||||
}
|
||||
|
||||
ResetState ()
|
||||
{
|
||||
this.mesh = new OV.Mesh ();
|
||||
this.model.AddMesh (this.mesh);
|
||||
this.triangle = null;
|
||||
}
|
||||
|
||||
CanImportExtension (extension)
|
||||
{
|
||||
return extension === 'stl';
|
||||
}
|
||||
|
||||
GetKnownFileFormats ()
|
||||
{
|
||||
return {
|
||||
'stl' : OV.FileFormat.Binary
|
||||
};
|
||||
}
|
||||
|
||||
GetUpDirection ()
|
||||
{
|
||||
return OV.Direction.Z;
|
||||
}
|
||||
|
||||
ImportContent (fileContent)
|
||||
{
|
||||
if (this.IsBinaryStlFile (fileContent)) {
|
||||
this.ProcessBinary (fileContent);
|
||||
} else {
|
||||
let obj = this;
|
||||
let textContent = OV.ArrayBufferToUtf8String (fileContent);
|
||||
OV.ReadLines (textContent, function (line) {
|
||||
if (!obj.IsError ()) {
|
||||
obj.ProcessLine (line);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
IsBinaryStlFile (fileContent)
|
||||
{
|
||||
let byteLength = fileContent.byteLength;
|
||||
if (byteLength < 84) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let reader = new OV.BinaryReader (fileContent, true);
|
||||
reader.Skip (80);
|
||||
|
||||
let triangleCount = reader.ReadUnsignedInteger32 ();
|
||||
if (byteLength !== triangleCount * 50 + 84) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ProcessLine (line)
|
||||
{
|
||||
if (line[0] === '#') {
|
||||
return;
|
||||
}
|
||||
|
||||
let parameters = OV.ParametersFromLine (line, '#');
|
||||
if (parameters.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let keyword = parameters[0];
|
||||
if (keyword === 'solid') {
|
||||
if (parameters.length > 1) {
|
||||
let name = OV.NameFromLine (line, keyword.length, '#');
|
||||
this.mesh.SetName (name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (keyword === 'facet') {
|
||||
this.triangle = new OV.Triangle (-1, -1, -1);
|
||||
if (parameters.length >= 5 && parameters[1] === 'normal') {
|
||||
let normalVector = new OV.Coord3D (
|
||||
parseFloat (parameters[2]),
|
||||
parseFloat (parameters[3]),
|
||||
parseFloat (parameters[4])
|
||||
);
|
||||
if (OV.IsPositive (normalVector.Length ())) {
|
||||
let normalIndex = this.mesh.AddNormal (normalVector);
|
||||
this.triangle.SetNormals (
|
||||
normalIndex,
|
||||
normalIndex,
|
||||
normalIndex
|
||||
);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (keyword === 'vertex' && this.triangle !== null) {
|
||||
if (parameters.length >= 4) {
|
||||
let vertexIndex = this.mesh.AddVertex (new OV.Coord3D (
|
||||
parseFloat (parameters[1]),
|
||||
parseFloat (parameters[2]),
|
||||
parseFloat (parameters[3])
|
||||
));
|
||||
if (this.triangle.v0 === -1) {
|
||||
this.triangle.v0 = vertexIndex;
|
||||
} else if (this.triangle.v1 === -1) {
|
||||
this.triangle.v1 = vertexIndex;
|
||||
} else if (this.triangle.v2 === -1) {
|
||||
this.triangle.v2 = vertexIndex;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (keyword === 'endfacet' && this.triangle !== null) {
|
||||
if (this.triangle.v0 !== -1 && this.triangle.v1 !== -1 && this.triangle.v2 !== null) {
|
||||
this.mesh.AddTriangle (this.triangle);
|
||||
}
|
||||
this.triangle = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ProcessBinary (fileContent)
|
||||
{
|
||||
function ReadVector (reader)
|
||||
{
|
||||
let coord = new OV.Coord3D ();
|
||||
coord.x = reader.ReadFloat32 ();
|
||||
coord.y = reader.ReadFloat32 ();
|
||||
coord.z = reader.ReadFloat32 ();
|
||||
return coord;
|
||||
}
|
||||
|
||||
function AddVertex (mesh, reader)
|
||||
{
|
||||
let coord = ReadVector (reader);
|
||||
return mesh.AddVertex (coord);
|
||||
}
|
||||
|
||||
let reader = new OV.BinaryReader (fileContent, true);
|
||||
reader.Skip (80);
|
||||
let triangleCount = reader.ReadUnsignedInteger32 ();
|
||||
for (let i = 0; i < triangleCount; i++) {
|
||||
let normalVector = ReadVector (reader);
|
||||
let v0 = AddVertex (this.mesh, reader);
|
||||
let v1 = AddVertex (this.mesh, reader);
|
||||
let v2 = AddVertex (this.mesh, reader);
|
||||
reader.Skip (2);
|
||||
let triangle = new OV.Triangle (v0, v1, v2);
|
||||
if (OV.IsPositive (normalVector.Length ())) {
|
||||
let normal = this.mesh.AddNormal (normalVector);
|
||||
triangle.SetNormals (normal, normal, normal);
|
||||
}
|
||||
this.mesh.AddTriangle (triangle);
|
||||
}
|
||||
}
|
||||
};
|
||||
72
source/import/importerutils.js
Normal file
72
source/import/importerutils.js
Normal file
@ -0,0 +1,72 @@
|
||||
OV.NameFromLine = function (line, startIndex, commentChar)
|
||||
{
|
||||
let name = line.substr (startIndex);
|
||||
let commentStart = name.indexOf (commentChar);
|
||||
if (commentStart !== -1) {
|
||||
name = name.substr (0, commentStart);
|
||||
}
|
||||
return name.trim ();
|
||||
};
|
||||
|
||||
OV.ParametersFromLine = function (line, commentChar)
|
||||
{
|
||||
if (commentChar !== null) {
|
||||
let commentStart = line.indexOf (commentChar);
|
||||
if (commentStart !== -1) {
|
||||
line = line.substr (0, commentStart).trim ();
|
||||
}
|
||||
}
|
||||
return line.split (/\s+/u);
|
||||
};
|
||||
|
||||
OV.ReadLines = function (str, onLine)
|
||||
{
|
||||
function LineFound (line, onLine)
|
||||
{
|
||||
let trimmed = line.trim ();
|
||||
if (trimmed.length > 0) {
|
||||
onLine (trimmed);
|
||||
}
|
||||
}
|
||||
|
||||
let cursor = 0;
|
||||
let next = str.indexOf ('\n', cursor);
|
||||
while (next !== -1) {
|
||||
LineFound (str.substr (cursor, next - cursor), onLine);
|
||||
cursor = next + 1;
|
||||
next = str.indexOf ('\n', cursor);
|
||||
}
|
||||
LineFound (str.substr (cursor), onLine);
|
||||
};
|
||||
|
||||
OV.IsPowerOfTwo = function (x)
|
||||
{
|
||||
return (x & (x - 1)) === 0;
|
||||
};
|
||||
|
||||
OV.NextPowerOfTwo = function (x)
|
||||
{
|
||||
if (OV.IsPowerOfTwo (x)) {
|
||||
return x;
|
||||
}
|
||||
let npot = Math.pow (2, Math.ceil (Math.log (x) / Math.log (2)));
|
||||
return parseInt (npot, 10);
|
||||
};
|
||||
|
||||
OV.ResizeImageToPowerOfTwoSides = function (image)
|
||||
{
|
||||
if (OV.IsPowerOfTwo (image.width) && OV.IsPowerOfTwo (image.height)) {
|
||||
return image;
|
||||
}
|
||||
|
||||
let width = OV.NextPowerOfTwo (image.width);
|
||||
let height = OV.NextPowerOfTwo (image.height);
|
||||
|
||||
let canvas = document.createElement ('canvas');
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
let context = canvas.getContext ('2d');
|
||||
context.drawImage (image, 0, 0, width, height);
|
||||
return context.getImageData (0, 0, width, height);
|
||||
};
|
||||
109
source/io/binaryreader.js
Normal file
109
source/io/binaryreader.js
Normal file
@ -0,0 +1,109 @@
|
||||
OV.BinaryReader = class
|
||||
{
|
||||
constructor (arrayBuffer, isLittleEndian)
|
||||
{
|
||||
this.arrayBuffer = arrayBuffer;
|
||||
this.dataView = new DataView (arrayBuffer);
|
||||
this.isLittleEndian = isLittleEndian;
|
||||
this.position = 0;
|
||||
}
|
||||
|
||||
GetPosition ()
|
||||
{
|
||||
return this.position;
|
||||
}
|
||||
|
||||
SetPosition (position)
|
||||
{
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
GetByteLength ()
|
||||
{
|
||||
return this.arrayBuffer.byteLength;
|
||||
}
|
||||
|
||||
Skip (bytes)
|
||||
{
|
||||
this.position = this.position + bytes;
|
||||
}
|
||||
|
||||
End ()
|
||||
{
|
||||
return this.position >= this.arrayBuffer.byteLength;
|
||||
}
|
||||
|
||||
ReadArrayBuffer (byteLength)
|
||||
{
|
||||
let originalBufferView = new Uint8Array (this.arrayBuffer);
|
||||
let arrayBuffer = new ArrayBuffer (byteLength);
|
||||
let bufferView = new Uint8Array (arrayBuffer);
|
||||
let subArray = originalBufferView.subarray (this.position, this.position + byteLength);
|
||||
bufferView.set (subArray, 0);
|
||||
this.position += byteLength;
|
||||
return arrayBuffer;
|
||||
}
|
||||
|
||||
ReadBoolean8 ()
|
||||
{
|
||||
let result = this.dataView.getInt8 (this.position);
|
||||
this.position = this.position + 1;
|
||||
return result ? true : false;
|
||||
}
|
||||
|
||||
ReadCharacter8 ()
|
||||
{
|
||||
let result = this.dataView.getInt8 (this.position);
|
||||
this.position = this.position + 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
ReadUnsignedCharacter8 ()
|
||||
{
|
||||
let result = this.dataView.getUint8 (this.position);
|
||||
this.position = this.position + 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
ReadInteger16 ()
|
||||
{
|
||||
let result = this.dataView.getInt16 (this.position, this.isLittleEndian);
|
||||
this.position = this.position + 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
ReadUnsignedInteger16 ()
|
||||
{
|
||||
let result = this.dataView.getUint16 (this.position, this.isLittleEndian);
|
||||
this.position = this.position + 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
ReadInteger32 ()
|
||||
{
|
||||
let result = this.dataView.getInt32 (this.position, this.isLittleEndian);
|
||||
this.position = this.position + 4;
|
||||
return result;
|
||||
}
|
||||
|
||||
ReadUnsignedInteger32 ()
|
||||
{
|
||||
let result = this.dataView.getUint32 (this.position, this.isLittleEndian);
|
||||
this.position = this.position + 4;
|
||||
return result;
|
||||
}
|
||||
|
||||
ReadFloat32 ()
|
||||
{
|
||||
let result = this.dataView.getFloat32 (this.position, this.isLittleEndian);
|
||||
this.position = this.position + 4;
|
||||
return result;
|
||||
}
|
||||
|
||||
ReadDouble64 ()
|
||||
{
|
||||
let result = this.dataView.getFloat64 (this.position, this.isLittleEndian);
|
||||
this.position = this.position + 8;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
92
source/io/binarywriter.js
Normal file
92
source/io/binarywriter.js
Normal file
@ -0,0 +1,92 @@
|
||||
OV.BinaryWriter = class
|
||||
{
|
||||
constructor (byteLength, isLittleEndian)
|
||||
{
|
||||
this.arrayBuffer = new ArrayBuffer (byteLength);
|
||||
this.dataView = new DataView (this.arrayBuffer);
|
||||
this.isLittleEndian = isLittleEndian;
|
||||
this.position = 0;
|
||||
}
|
||||
|
||||
GetPosition ()
|
||||
{
|
||||
return this.position;
|
||||
}
|
||||
|
||||
SetPosition (position)
|
||||
{
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
End ()
|
||||
{
|
||||
return this.position >= this.arrayBuffer.byteLength;
|
||||
}
|
||||
|
||||
GetBuffer ()
|
||||
{
|
||||
return this.arrayBuffer;
|
||||
}
|
||||
|
||||
WriteArrayBuffer (arrayBuffer)
|
||||
{
|
||||
let bufferView = new Uint8Array (arrayBuffer);
|
||||
let thisBufferView = new Uint8Array (this.arrayBuffer);
|
||||
thisBufferView.set (bufferView, this.position);
|
||||
this.position += arrayBuffer.byteLength;
|
||||
}
|
||||
|
||||
WriteBoolean8 (val)
|
||||
{
|
||||
this.dataView.setInt8 (this.position, val ? 1 : 0);
|
||||
this.position = this.position + 1;
|
||||
}
|
||||
|
||||
WriteCharacter8 (val)
|
||||
{
|
||||
this.dataView.setInt8 (this.position, val);
|
||||
this.position = this.position + 1;
|
||||
}
|
||||
|
||||
WriteUnsignedCharacter8 (val)
|
||||
{
|
||||
this.dataView.setUint8 (this.position, val);
|
||||
this.position = this.position + 1;
|
||||
}
|
||||
|
||||
WriteInteger16 (val)
|
||||
{
|
||||
this.dataView.setInt16 (this.position, val, this.isLittleEndian);
|
||||
this.position = this.position + 2;
|
||||
}
|
||||
|
||||
WriteUnsignedInteger16 (val)
|
||||
{
|
||||
this.dataView.setUint16 (this.position, val, this.isLittleEndian);
|
||||
this.position = this.position + 2;
|
||||
}
|
||||
|
||||
WriteInteger32 (val)
|
||||
{
|
||||
this.dataView.setInt32 (this.position, val, this.isLittleEndian);
|
||||
this.position = this.position + 4;
|
||||
}
|
||||
|
||||
WriteUnsignedInteger32 (val)
|
||||
{
|
||||
this.dataView.setUint32 (this.position, val, this.isLittleEndian);
|
||||
this.position = this.position + 4;
|
||||
}
|
||||
|
||||
WriteFloat32 (val)
|
||||
{
|
||||
this.dataView.setFloat32 (this.position, val, this.isLittleEndian);
|
||||
this.position = this.position + 4;
|
||||
}
|
||||
|
||||
WriteDouble64 (val)
|
||||
{
|
||||
this.dataView.setFloat64 (this.position, val, this.isLittleEndian);
|
||||
this.position = this.position + 8;
|
||||
}
|
||||
};
|
||||
56
source/io/bufferutils.js
Normal file
56
source/io/bufferutils.js
Normal file
@ -0,0 +1,56 @@
|
||||
OV.ArrayBufferToUtf8String = function (buffer)
|
||||
{
|
||||
let decoder = new TextDecoder ('utf-8');
|
||||
return decoder.decode (buffer);
|
||||
};
|
||||
|
||||
OV.ArrayBufferToAsciiString = function (buffer)
|
||||
{
|
||||
let text = '';
|
||||
let bufferView = new Uint8Array (buffer);
|
||||
for (let i = 0; i < bufferView.byteLength; i++) {
|
||||
text += String.fromCharCode (bufferView[i]);
|
||||
}
|
||||
return text;
|
||||
};
|
||||
|
||||
OV.AsciiStringToArrayBuffer = function (str)
|
||||
{
|
||||
let buffer = new ArrayBuffer (str.length);
|
||||
let bufferView = new Uint8Array (buffer);
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
bufferView[i] = str.charCodeAt (i);
|
||||
}
|
||||
return buffer;
|
||||
};
|
||||
|
||||
OV.Base64DataURIToArrayBuffer = function (uri)
|
||||
{
|
||||
let dataPrefix = 'data:';
|
||||
if (!uri.startsWith (dataPrefix)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let mimeSeparator = uri.indexOf (';');
|
||||
if (mimeSeparator === -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let bufferSeparator = uri.indexOf (',');
|
||||
if (bufferSeparator === -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let mimeType = uri.substr (dataPrefix.length, mimeSeparator - 5);
|
||||
let base64String = atob (uri.substr (bufferSeparator + 1));
|
||||
let buffer = new ArrayBuffer (base64String.length);
|
||||
let bufferView = new Uint8Array (buffer);
|
||||
for (let i = 0; i < base64String.length; i++) {
|
||||
bufferView[i] = base64String.charCodeAt (i);
|
||||
}
|
||||
|
||||
return {
|
||||
mimeType : mimeType,
|
||||
buffer : buffer
|
||||
};
|
||||
};
|
||||
125
source/io/fileutils.js
Normal file
125
source/io/fileutils.js
Normal file
@ -0,0 +1,125 @@
|
||||
OV.FileSource =
|
||||
{
|
||||
Url : 1,
|
||||
File : 2
|
||||
};
|
||||
|
||||
OV.FileFormat =
|
||||
{
|
||||
Text : 1,
|
||||
Binary : 2
|
||||
};
|
||||
|
||||
OV.GetFileName = function (filePath)
|
||||
{
|
||||
let firstSeparator = filePath.lastIndexOf ('/');
|
||||
if (firstSeparator === -1) {
|
||||
firstSeparator = filePath.lastIndexOf ('\\');
|
||||
}
|
||||
let fileName = filePath;
|
||||
if (firstSeparator !== -1) {
|
||||
fileName = filePath.substr (firstSeparator + 1);
|
||||
}
|
||||
return decodeURI (fileName);
|
||||
};
|
||||
|
||||
OV.GetFileExtension = function (fileName)
|
||||
{
|
||||
let firstPoint = fileName.lastIndexOf ('.');
|
||||
if (firstPoint === -1) {
|
||||
return '';
|
||||
}
|
||||
return fileName.substr (firstPoint + 1);
|
||||
};
|
||||
|
||||
OV.RequestUrl = function (url, format, callbacks)
|
||||
{
|
||||
function OnSuccess (result)
|
||||
{
|
||||
if (callbacks.success) {
|
||||
callbacks.success (result);
|
||||
}
|
||||
if (callbacks.complete) {
|
||||
callbacks.complete ();
|
||||
}
|
||||
}
|
||||
|
||||
function OnError ()
|
||||
{
|
||||
if (callbacks.error) {
|
||||
callbacks.error ();
|
||||
}
|
||||
if (callbacks.complete) {
|
||||
callbacks.complete ();
|
||||
}
|
||||
}
|
||||
|
||||
let request = new XMLHttpRequest ();
|
||||
request.open ('GET', url, true);
|
||||
if (format === OV.FileFormat.Text) {
|
||||
request.responseType = 'text';
|
||||
} else if (format === OV.FileFormat.Binary) {
|
||||
request.responseType = 'arraybuffer';
|
||||
} else {
|
||||
OnError ();
|
||||
return;
|
||||
}
|
||||
|
||||
request.onload = function () {
|
||||
if (request.status === 200) {
|
||||
let response = request.response;
|
||||
OnSuccess (response);
|
||||
} else {
|
||||
OnError ();
|
||||
}
|
||||
};
|
||||
|
||||
request.onerror = function () {
|
||||
OnError ();
|
||||
};
|
||||
|
||||
request.send (null);
|
||||
};
|
||||
|
||||
OV.ReadFile = function (file, format, callbacks)
|
||||
{
|
||||
function OnSuccess (result)
|
||||
{
|
||||
if (callbacks.success) {
|
||||
callbacks.success (result);
|
||||
}
|
||||
if (callbacks.complete) {
|
||||
callbacks.complete ();
|
||||
}
|
||||
}
|
||||
|
||||
function OnError ()
|
||||
{
|
||||
if (callbacks.error) {
|
||||
callbacks.error ();
|
||||
}
|
||||
if (callbacks.complete) {
|
||||
callbacks.complete ();
|
||||
}
|
||||
}
|
||||
|
||||
let reader = new FileReader ();
|
||||
|
||||
reader.onloadend = function (event) {
|
||||
if (event.target.readyState === FileReader.DONE) {
|
||||
OnSuccess (event.target.result);
|
||||
}
|
||||
};
|
||||
|
||||
reader.onerror = function () {
|
||||
OnError ();
|
||||
};
|
||||
|
||||
if (format === OV.FileFormat.Text) {
|
||||
reader.readAsText (file);
|
||||
} else if (format === OV.FileFormat.Binary) {
|
||||
reader.readAsArrayBuffer (file);
|
||||
} else {
|
||||
OnError ();
|
||||
}
|
||||
};
|
||||
41
source/io/textwriter.js
Normal file
41
source/io/textwriter.js
Normal file
@ -0,0 +1,41 @@
|
||||
OV.TextWriter = class
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
this.text = '';
|
||||
this.indentation = 0;
|
||||
}
|
||||
|
||||
GetText ()
|
||||
{
|
||||
return this.text;
|
||||
}
|
||||
|
||||
Indent (diff)
|
||||
{
|
||||
this.indentation += diff;
|
||||
}
|
||||
|
||||
WriteArrayLine (arr)
|
||||
{
|
||||
this.WriteLine (arr.join (' '));
|
||||
}
|
||||
|
||||
WriteLine (str)
|
||||
{
|
||||
this.WriteIndentation ();
|
||||
this.Write (str + '\n');
|
||||
}
|
||||
|
||||
WriteIndentation ()
|
||||
{
|
||||
for (let i = 0; i < this.indentation; i++) {
|
||||
this.Write (' ');
|
||||
}
|
||||
}
|
||||
|
||||
Write (str)
|
||||
{
|
||||
this.text += str;
|
||||
}
|
||||
};
|
||||
100
source/model/mesh.js
Normal file
100
source/model/mesh.js
Normal file
@ -0,0 +1,100 @@
|
||||
OV.Mesh = class
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
this.name = '';
|
||||
this.vertices = [];
|
||||
this.normals = [];
|
||||
this.uvs = [];
|
||||
this.triangles = [];
|
||||
}
|
||||
|
||||
GetName ()
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
|
||||
SetName (name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
VertexCount ()
|
||||
{
|
||||
return this.vertices.length;
|
||||
}
|
||||
|
||||
NormalCount ()
|
||||
{
|
||||
return this.normals.length;
|
||||
}
|
||||
|
||||
TextureUVCount ()
|
||||
{
|
||||
return this.uvs.length;
|
||||
}
|
||||
|
||||
TriangleCount ()
|
||||
{
|
||||
return this.triangles.length;
|
||||
}
|
||||
|
||||
AddVertex (vertex)
|
||||
{
|
||||
this.vertices.push (vertex);
|
||||
return this.vertices.length - 1;
|
||||
}
|
||||
|
||||
SetVertex (index, vertex)
|
||||
{
|
||||
this.vertices[index] = vertex;
|
||||
}
|
||||
|
||||
GetVertex (index)
|
||||
{
|
||||
return this.vertices[index];
|
||||
}
|
||||
|
||||
AddNormal (normal)
|
||||
{
|
||||
this.normals.push (normal);
|
||||
return this.normals.length - 1;
|
||||
}
|
||||
|
||||
SetNormal (index, normal)
|
||||
{
|
||||
this.normals[index] = normal;
|
||||
}
|
||||
|
||||
GetNormal (index)
|
||||
{
|
||||
return this.normals[index];
|
||||
}
|
||||
|
||||
AddTextureUV (uv)
|
||||
{
|
||||
this.uvs.push (uv);
|
||||
return this.uvs.length - 1;
|
||||
}
|
||||
|
||||
SetTextureUV (index, uv)
|
||||
{
|
||||
this.uvs[index] = uv;
|
||||
}
|
||||
|
||||
GetTextureUV (index)
|
||||
{
|
||||
return this.uvs[index];
|
||||
}
|
||||
|
||||
AddTriangle (triangle)
|
||||
{
|
||||
this.triangles.push (triangle);
|
||||
return this.triangles.length - 1;
|
||||
}
|
||||
|
||||
GetTriangle (index)
|
||||
{
|
||||
return this.triangles[index];
|
||||
}
|
||||
};
|
||||
144
source/model/meshbuffer.js
Normal file
144
source/model/meshbuffer.js
Normal file
@ -0,0 +1,144 @@
|
||||
OV.MeshPrimitiveBuffer = class
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
this.indices = [];
|
||||
this.vertices = [];
|
||||
this.normals = [];
|
||||
this.uvs = [];
|
||||
this.material = null;
|
||||
}
|
||||
|
||||
GetByteLength (indexTypeSize, numberTypeSize)
|
||||
{
|
||||
let indexCount = this.indices.length;
|
||||
let numberCount = this.vertices.length + this.normals.length + this.uvs.length;
|
||||
return indexCount * indexTypeSize + numberCount * numberTypeSize;
|
||||
}
|
||||
};
|
||||
|
||||
OV.MeshBuffer = class
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
this.primitives = [];
|
||||
}
|
||||
|
||||
GetByteLength (indexTypeSize, numberTypeSize)
|
||||
{
|
||||
let byteLength = 0;
|
||||
for (let i = 0; i < this.primitives.length; i++) {
|
||||
let primitive = this.primitives[i];
|
||||
byteLength += primitive.GetByteLength (indexTypeSize, numberTypeSize);
|
||||
}
|
||||
return byteLength;
|
||||
}
|
||||
};
|
||||
|
||||
OV.ConvertMeshToMeshBuffer = function (mesh)
|
||||
{
|
||||
function AddVertexToPrimitiveBuffer (mesh, vertexIndex, normalIndex, uvIndex, primitiveBuffer, primitiveMaps)
|
||||
{
|
||||
function GetUVOrDefault (mesh, uvIndex, forceUVs)
|
||||
{
|
||||
if (uvIndex !== null) {
|
||||
return mesh.GetTextureUV (uvIndex);
|
||||
} else if (forceUVs) {
|
||||
return new OV.Coord2D (0.0, 0.0);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function AddVertex (mesh, vertexIndex, normalIndex, uvIndex, primitiveBuffer)
|
||||
{
|
||||
let forceUVs = mesh.TextureUVCount () > 0;
|
||||
let vertex = mesh.GetVertex (vertexIndex);
|
||||
let normal = mesh.GetNormal (normalIndex);
|
||||
let primitiveVertexIndex = primitiveBuffer.vertices.length / 3;
|
||||
primitiveBuffer.indices.push (primitiveVertexIndex);
|
||||
primitiveBuffer.vertices.push (vertex.x, vertex.y, vertex.z);
|
||||
primitiveBuffer.normals.push (normal.x, normal.y, normal.z);
|
||||
let uv = GetUVOrDefault (mesh, uvIndex, forceUVs);
|
||||
if (uv !== null) {
|
||||
primitiveBuffer.uvs.push (uv.x, uv.y);
|
||||
}
|
||||
return {
|
||||
index : primitiveVertexIndex,
|
||||
normal : normal,
|
||||
uv : uv
|
||||
};
|
||||
}
|
||||
|
||||
function FindMatchingPrimitiveVertex (mesh, primitiveVertices, normalIndex, uvIndex)
|
||||
{
|
||||
for (let i = 0; i < primitiveVertices.length; i++) {
|
||||
let primitiveVertex = primitiveVertices[i];
|
||||
let normal = mesh.GetNormal (normalIndex);
|
||||
let equalNormal = OV.CoordIsEqual3D (primitiveVertex.normal, normal);
|
||||
let equalUv = false;
|
||||
if (primitiveVertex.uv === null && uvIndex === null) {
|
||||
equalUv = true;
|
||||
} else {
|
||||
let uv = GetUVOrDefault (mesh, uvIndex, true);
|
||||
equalUv = OV.CoordIsEqual2D (primitiveVertex.uv, uv);
|
||||
}
|
||||
if (equalNormal && equalUv) {
|
||||
return primitiveVertex;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
let primitiveVertices = primitiveMaps.meshToPrimitiveVertices[vertexIndex];
|
||||
if (primitiveVertices === undefined) {
|
||||
let primitiveVertex = AddVertex (mesh, vertexIndex, normalIndex, uvIndex, primitiveBuffer);
|
||||
primitiveMaps.meshToPrimitiveVertices[vertexIndex] = [primitiveVertex];
|
||||
} else {
|
||||
let existingPrimitiveVertex = FindMatchingPrimitiveVertex (mesh, primitiveVertices, normalIndex, uvIndex);
|
||||
if (existingPrimitiveVertex !== null) {
|
||||
primitiveBuffer.indices.push (existingPrimitiveVertex.index);
|
||||
} else {
|
||||
let primitiveVertex = AddVertex (mesh, vertexIndex, normalIndex, uvIndex, primitiveBuffer);
|
||||
primitiveVertices.push (primitiveVertex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let meshBuffer = new OV.MeshBuffer ();
|
||||
|
||||
let triangleCount = mesh.TriangleCount ();
|
||||
if (triangleCount === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let triangleIndices = [];
|
||||
for (let i = 0; i < triangleCount; i++) {
|
||||
triangleIndices.push (i);
|
||||
}
|
||||
triangleIndices.sort (function (a, b) {
|
||||
let aTriangle = mesh.GetTriangle (a);
|
||||
let bTriangle = mesh.GetTriangle (b);
|
||||
return aTriangle.mat - bTriangle.mat;
|
||||
});
|
||||
|
||||
let primitiveBuffer = null;
|
||||
let primitiveMaps = null;
|
||||
for (let i = 0; i < triangleIndices.length; i++) {
|
||||
let triangleIndex = triangleIndices[i];
|
||||
let triangle = mesh.GetTriangle (triangleIndex);
|
||||
if (primitiveBuffer === null || primitiveBuffer.material !== triangle.mat) {
|
||||
primitiveBuffer = new OV.MeshPrimitiveBuffer ();
|
||||
primitiveBuffer.material = triangle.mat;
|
||||
primitiveMaps = {
|
||||
meshToPrimitiveVertices : {}
|
||||
};
|
||||
meshBuffer.primitives.push (primitiveBuffer);
|
||||
}
|
||||
AddVertexToPrimitiveBuffer (mesh, triangle.v0, triangle.n0, triangle.u0, primitiveBuffer, primitiveMaps);
|
||||
AddVertexToPrimitiveBuffer (mesh, triangle.v1, triangle.n1, triangle.u1, primitiveBuffer, primitiveMaps);
|
||||
AddVertexToPrimitiveBuffer (mesh, triangle.v2, triangle.n2, triangle.u2, primitiveBuffer, primitiveMaps);
|
||||
}
|
||||
|
||||
return meshBuffer;
|
||||
};
|
||||
105
source/model/model.js
Normal file
105
source/model/model.js
Normal file
@ -0,0 +1,105 @@
|
||||
OV.Model = class
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
this.name = '';
|
||||
this.materials = [];
|
||||
this.meshes = [];
|
||||
}
|
||||
|
||||
GetName ()
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
|
||||
SetName (name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
MaterialCount ()
|
||||
{
|
||||
return this.materials.length;
|
||||
}
|
||||
|
||||
MeshCount ()
|
||||
{
|
||||
return this.meshes.length;
|
||||
}
|
||||
|
||||
VertexCount ()
|
||||
{
|
||||
let count = 0;
|
||||
for (let i = 0; i < this.meshes.length; i++) {
|
||||
count += this.meshes[i].VertexCount ();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
NormalCount ()
|
||||
{
|
||||
let count = 0;
|
||||
for (let i = 0; i < this.meshes.length; i++) {
|
||||
count += this.meshes[i].NormalCount ();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
TextureUVCount ()
|
||||
{
|
||||
let count = 0;
|
||||
for (let i = 0; i < this.meshes.length; i++) {
|
||||
count += this.meshes[i].TextureUVCount ();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
TriangleCount ()
|
||||
{
|
||||
let count = 0;
|
||||
for (let i = 0; i < this.meshes.length; i++) {
|
||||
count += this.meshes[i].TriangleCount ();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
AddMaterial (material)
|
||||
{
|
||||
this.materials.push (material);
|
||||
return this.materials.length - 1;
|
||||
}
|
||||
|
||||
GetMaterial (index)
|
||||
{
|
||||
return this.materials[index];
|
||||
}
|
||||
|
||||
AddMesh (mesh)
|
||||
{
|
||||
this.meshes.push (mesh);
|
||||
return this.meshes.length - 1;
|
||||
}
|
||||
|
||||
AddMeshToIndex (mesh, index)
|
||||
{
|
||||
this.meshes.splice (index, 0, mesh);
|
||||
return index;
|
||||
}
|
||||
|
||||
RemoveMesh (index)
|
||||
{
|
||||
this.meshes.splice (index, 1);
|
||||
}
|
||||
|
||||
GetMesh (index)
|
||||
{
|
||||
return this.meshes[index];
|
||||
}
|
||||
|
||||
Clear ()
|
||||
{
|
||||
this.name = '';
|
||||
this.materials = [];
|
||||
this.meshes = [];
|
||||
}
|
||||
};
|
||||
229
source/model/modelentities.js
Normal file
229
source/model/modelentities.js
Normal file
@ -0,0 +1,229 @@
|
||||
OV.SRGBToLinear = function (component)
|
||||
{
|
||||
if (component < 0.04045) {
|
||||
return component * 0.0773993808;
|
||||
} else {
|
||||
return Math.pow (component * 0.9478672986 + 0.0521327014, 2.4);
|
||||
}
|
||||
};
|
||||
|
||||
OV.LinearToSRGB = function (component)
|
||||
{
|
||||
if (component < 0.0031308) {
|
||||
return component * 12.92;
|
||||
} else {
|
||||
return 1.055 * (Math.pow (component, 0.41666)) - 0.055;
|
||||
}
|
||||
};
|
||||
|
||||
OV.Color = class
|
||||
{
|
||||
constructor (r, g, b)
|
||||
{
|
||||
this.r = r; // 0 .. 255
|
||||
this.g = g; // 0 .. 255
|
||||
this.b = b; // 0 .. 255
|
||||
}
|
||||
|
||||
ToHexString ()
|
||||
{
|
||||
function IntegerToHex (intVal)
|
||||
{
|
||||
let result = parseInt (intVal, 10).toString (16);
|
||||
while (result.length < 2) {
|
||||
result = '0' + result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
let r = IntegerToHex (this.r);
|
||||
let g = IntegerToHex (this.g);
|
||||
let b = IntegerToHex (this.b);
|
||||
return r + g + b;
|
||||
}
|
||||
|
||||
Clone ()
|
||||
{
|
||||
return new OV.Color (this.r, this.g, this.b);
|
||||
}
|
||||
};
|
||||
|
||||
OV.TextureMap = class
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
this.name = null;
|
||||
this.url = null;
|
||||
this.offset = new OV.Coord2D (0.0, 0.0);
|
||||
this.scale = new OV.Coord2D (1.0, 1.0);
|
||||
this.rotation = 0.0; // radians
|
||||
}
|
||||
|
||||
HasTransformation ()
|
||||
{
|
||||
if (!OV.CoordIsEqual2D (this.offset, new OV.Coord2D (0.0, 0.0))) {
|
||||
return true;
|
||||
}
|
||||
if (!OV.CoordIsEqual2D (this.scale, new OV.Coord2D (1.0, 1.0))) {
|
||||
return true;
|
||||
}
|
||||
if (!OV.IsEqual (this.rotation, 0.0)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Clone ()
|
||||
{
|
||||
let cloned = new OV.TextureMap ();
|
||||
cloned.name = this.name;
|
||||
cloned.url = this.url;
|
||||
cloned.offset = this.offset.Clone ();
|
||||
cloned.scale = this.scale.Clone ();
|
||||
cloned.rotation = this.rotation;
|
||||
return cloned;
|
||||
}
|
||||
};
|
||||
|
||||
OV.Material = class
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
this.name = '';
|
||||
|
||||
this.ambient = new OV.Color (0, 0, 0);
|
||||
this.diffuse = new OV.Color (0, 0, 0);
|
||||
this.specular = new OV.Color (0, 0, 0);
|
||||
this.emissive = new OV.Color (0, 0, 0);
|
||||
|
||||
this.shininess = 0.0; // 0.0 .. 1.0
|
||||
this.opacity = 1.0; // 0.0 .. 1.0
|
||||
|
||||
this.diffuseMap = null;
|
||||
this.specularMap = null;
|
||||
this.bumpMap = null;
|
||||
this.normalMap = null;
|
||||
this.emissiveMap = null;
|
||||
|
||||
this.alphaTest = 0.0; // 0.0 .. 1.0
|
||||
this.transparent = false;
|
||||
this.multiplyDiffuseMap = false;
|
||||
}
|
||||
|
||||
Clone ()
|
||||
{
|
||||
let cloned = new OV.Material ();
|
||||
|
||||
cloned.name = this.name;
|
||||
|
||||
cloned.ambient = this.ambient.Clone ();
|
||||
cloned.diffuse = this.diffuse.Clone ();
|
||||
cloned.specular = this.specular.Clone ();
|
||||
cloned.emissive = this.emissive.Clone ();
|
||||
|
||||
cloned.shininess = this.shininess;
|
||||
cloned.opacity = this.opacity;
|
||||
|
||||
cloned.diffuseMap = this.CloneTextureMap (this.diffuseMap);
|
||||
cloned.specularMap = this.CloneTextureMap (this.specularMap);
|
||||
cloned.bumpMap = this.CloneTextureMap (this.bumpMap);
|
||||
cloned.normalMap = this.CloneTextureMap (this.normalMap);
|
||||
cloned.emissiveMap = this.CloneTextureMap (this.emissiveMap);
|
||||
|
||||
cloned.alphaTest = this.alphaTest;
|
||||
cloned.transparent = this.transparent;
|
||||
cloned.multiplyDiffuseMap = this.multiplyDiffuseMap;
|
||||
|
||||
return cloned;
|
||||
}
|
||||
|
||||
CloneTextureMap (textureMap)
|
||||
{
|
||||
if (textureMap === null) {
|
||||
return null;
|
||||
}
|
||||
return textureMap.Clone ();
|
||||
}
|
||||
};
|
||||
|
||||
OV.Triangle = class
|
||||
{
|
||||
constructor (v0, v1, v2)
|
||||
{
|
||||
this.v0 = v0;
|
||||
this.v1 = v1;
|
||||
this.v2 = v2;
|
||||
|
||||
this.n0 = null;
|
||||
this.n1 = null;
|
||||
this.n2 = null;
|
||||
|
||||
this.u0 = null;
|
||||
this.u1 = null;
|
||||
this.u2 = null;
|
||||
|
||||
this.mat = null;
|
||||
this.curve = null;
|
||||
}
|
||||
|
||||
HasVertices ()
|
||||
{
|
||||
return this.v0 !== null && this.v1 !== null && this.v2 !== null;
|
||||
}
|
||||
|
||||
HasNormals ()
|
||||
{
|
||||
return this.n0 !== null && this.n1 !== null && this.n2 !== null;
|
||||
}
|
||||
|
||||
HasTextureUVs ()
|
||||
{
|
||||
return this.u0 !== null && this.u1 !== null && this.u2 !== null;
|
||||
}
|
||||
|
||||
SetVertices (v0, v1, v2)
|
||||
{
|
||||
this.v0 = v0;
|
||||
this.v1 = v1;
|
||||
this.v2 = v2;
|
||||
return this;
|
||||
}
|
||||
|
||||
SetNormals (n0, n1, n2)
|
||||
{
|
||||
this.n0 = n0;
|
||||
this.n1 = n1;
|
||||
this.n2 = n2;
|
||||
return this;
|
||||
}
|
||||
|
||||
SetTextureUVs (u0, u1, u2)
|
||||
{
|
||||
this.u0 = u0;
|
||||
this.u1 = u1;
|
||||
this.u2 = u2;
|
||||
return this;
|
||||
}
|
||||
|
||||
SetMaterial (mat)
|
||||
{
|
||||
this.mat = mat;
|
||||
return this;
|
||||
}
|
||||
|
||||
SetCurve (curve)
|
||||
{
|
||||
this.curve = curve;
|
||||
return this;
|
||||
}
|
||||
|
||||
Clone ()
|
||||
{
|
||||
let cloned = new OV.Triangle (this.v0, this.v1, this.v2);
|
||||
cloned.SetNormals (this.n0, this.n1, this.n2);
|
||||
cloned.SetTextureUVs (this.u0, this.u1, this.u2);
|
||||
cloned.SetMaterial (this.mat);
|
||||
cloned.SetCurve (this.curve);
|
||||
return cloned;
|
||||
}
|
||||
};
|
||||
241
source/model/modelfinalization.js
Normal file
241
source/model/modelfinalization.js
Normal file
@ -0,0 +1,241 @@
|
||||
OV.FinalizeModel = function (model, getDefaultMaterial)
|
||||
{
|
||||
function FinalizeMesh (mesh, getDefaultMaterialIndex)
|
||||
{
|
||||
function CalculateCurveNormals (mesh)
|
||||
{
|
||||
function AddAverageNormal (mesh, triangle, vertexIndex, triangleNormals, vertexToTriangles)
|
||||
{
|
||||
let averageNormal = new OV.Coord3D (0.0, 0.0, 0.0);
|
||||
let averageCount = 0;
|
||||
|
||||
let neigTriangles = vertexToTriangles[vertexIndex];
|
||||
for (let i = 0; i < neigTriangles.length; i++) {
|
||||
let neigIndex = neigTriangles[i];
|
||||
let neigTriangle = mesh.GetTriangle (neigIndex);
|
||||
if (triangle.curve === neigTriangle.curve) {
|
||||
averageNormal = OV.AddCoord3D (averageNormal, triangleNormals[neigIndex]);
|
||||
averageCount = averageCount + 1;
|
||||
}
|
||||
}
|
||||
|
||||
averageNormal.MultiplyScalar (1.0 / averageCount);
|
||||
averageNormal.Normalize ();
|
||||
return mesh.AddNormal (averageNormal);
|
||||
}
|
||||
|
||||
let triangleNormals = [];
|
||||
let vertexToTriangles = {};
|
||||
|
||||
for (let vertexIndex = 0; vertexIndex < mesh.VertexCount (); vertexIndex++) {
|
||||
vertexToTriangles[vertexIndex] = [];
|
||||
}
|
||||
|
||||
for (let triangleIndex = 0; triangleIndex < mesh.TriangleCount (); triangleIndex++) {
|
||||
let triangle = mesh.GetTriangle (triangleIndex);
|
||||
let v0 = mesh.GetVertex (triangle.v0);
|
||||
let v1 = mesh.GetVertex (triangle.v1);
|
||||
let v2 = mesh.GetVertex (triangle.v2);
|
||||
let normal = OV.CalculateTriangleNormal (v0, v1, v2);
|
||||
triangleNormals.push (normal);
|
||||
vertexToTriangles[triangle.v0].push (triangleIndex);
|
||||
vertexToTriangles[triangle.v1].push (triangleIndex);
|
||||
vertexToTriangles[triangle.v2].push (triangleIndex);
|
||||
}
|
||||
|
||||
for (let triangleIndex = 0; triangleIndex < mesh.TriangleCount (); triangleIndex++) {
|
||||
let triangle = mesh.GetTriangle (triangleIndex);
|
||||
if (triangle.n0 === null || triangle.n1 === null || triangle.n2 === null) {
|
||||
let n0 = AddAverageNormal (mesh, triangle, triangle.v0, triangleNormals, vertexToTriangles);
|
||||
let n1 = AddAverageNormal (mesh, triangle, triangle.v1, triangleNormals, vertexToTriangles);
|
||||
let n2 = AddAverageNormal (mesh, triangle, triangle.v2, triangleNormals, vertexToTriangles);
|
||||
triangle.SetNormals (n0, n1, n2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function FinalizeTriangle (mesh, triangle, status)
|
||||
{
|
||||
if (!triangle.HasNormals ()) {
|
||||
if (triangle.curve === null || triangle.curve === 0) {
|
||||
let v0 = mesh.GetVertex (triangle.v0);
|
||||
let v1 = mesh.GetVertex (triangle.v1);
|
||||
let v2 = mesh.GetVertex (triangle.v2);
|
||||
let normal = OV.CalculateTriangleNormal (v0, v1, v2);
|
||||
let normalIndex = mesh.AddNormal (normal);
|
||||
triangle.SetNormals (normalIndex, normalIndex, normalIndex);
|
||||
} else {
|
||||
status.calculateCurveNormals = true;
|
||||
}
|
||||
}
|
||||
if (triangle.mat === null) {
|
||||
triangle.mat = status.getDefaultMaterialIndex ();
|
||||
}
|
||||
if (triangle.curve === null) {
|
||||
triangle.curve = 0;
|
||||
}
|
||||
}
|
||||
|
||||
let status = {
|
||||
getDefaultMaterialIndex : getDefaultMaterialIndex,
|
||||
calculateCurveNormals : false
|
||||
};
|
||||
|
||||
for (let i = 0; i < mesh.TriangleCount (); i++) {
|
||||
let triangle = mesh.GetTriangle (i);
|
||||
FinalizeTriangle (mesh, triangle, status);
|
||||
}
|
||||
|
||||
if (status.calculateCurveNormals) {
|
||||
CalculateCurveNormals (mesh);
|
||||
}
|
||||
}
|
||||
|
||||
let defaultMaterialIndex = null;
|
||||
let getDefaultMaterialIndex = function () {
|
||||
if (defaultMaterialIndex === null) {
|
||||
defaultMaterialIndex = model.AddMaterial (getDefaultMaterial ());
|
||||
}
|
||||
return defaultMaterialIndex;
|
||||
};
|
||||
|
||||
for (let i = 0; i < model.MeshCount (); i++) {
|
||||
let mesh = model.GetMesh (i);
|
||||
if (mesh.TriangleCount () === 0) {
|
||||
model.RemoveMesh (i);
|
||||
i = i - 1;
|
||||
continue;
|
||||
}
|
||||
FinalizeMesh (mesh, getDefaultMaterialIndex);
|
||||
}
|
||||
};
|
||||
|
||||
OV.CheckModel = function (model)
|
||||
{
|
||||
function IsCorrectValue (val)
|
||||
{
|
||||
if (val === undefined || val === null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function IsCorrectNumber (val)
|
||||
{
|
||||
if (!IsCorrectValue (val)) {
|
||||
return false;
|
||||
}
|
||||
if (isNaN (val)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function IsCorrectIndex (val, count)
|
||||
{
|
||||
if (!IsCorrectNumber (val)) {
|
||||
return false;
|
||||
}
|
||||
if (val < 0 || val >= count) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function CheckMesh (model, mesh)
|
||||
{
|
||||
function CheckTriangle (model, mesh, triangle)
|
||||
{
|
||||
if (!IsCorrectIndex (triangle.v0, mesh.VertexCount ())) {
|
||||
return false;
|
||||
}
|
||||
if (!IsCorrectIndex (triangle.v1, mesh.VertexCount ())) {
|
||||
return false;
|
||||
}
|
||||
if (!IsCorrectIndex (triangle.v2, mesh.VertexCount ())) {
|
||||
return false;
|
||||
}
|
||||
if (!IsCorrectIndex (triangle.n0, mesh.NormalCount ())) {
|
||||
return false;
|
||||
}
|
||||
if (!IsCorrectIndex (triangle.n1, mesh.NormalCount ())) {
|
||||
return false;
|
||||
}
|
||||
if (!IsCorrectIndex (triangle.n2, mesh.NormalCount ())) {
|
||||
return false;
|
||||
}
|
||||
if (triangle.HasTextureUVs ()) {
|
||||
if (!IsCorrectIndex (triangle.u0, mesh.TextureUVCount ())) {
|
||||
return false;
|
||||
}
|
||||
if (!IsCorrectIndex (triangle.u1, mesh.TextureUVCount ())) {
|
||||
return false;
|
||||
}
|
||||
if (!IsCorrectIndex (triangle.u2, mesh.TextureUVCount ())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!IsCorrectIndex (triangle.mat, model.MaterialCount ())) {
|
||||
return false;
|
||||
}
|
||||
if (!IsCorrectNumber (triangle.curve)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
for (let i = 0; i < mesh.VertexCount (); i++) {
|
||||
let vertex = mesh.GetVertex (i);
|
||||
if (!IsCorrectNumber (vertex.x)) {
|
||||
return false;
|
||||
}
|
||||
if (!IsCorrectNumber (vertex.y)) {
|
||||
return false;
|
||||
}
|
||||
if (!IsCorrectNumber (vertex.z)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < mesh.NormalCount (); i++) {
|
||||
let normal = mesh.GetNormal (i);
|
||||
if (!IsCorrectNumber (normal.x)) {
|
||||
return false;
|
||||
}
|
||||
if (!IsCorrectNumber (normal.y)) {
|
||||
return false;
|
||||
}
|
||||
if (!IsCorrectNumber (normal.z)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < mesh.TextureUVCount (); i++) {
|
||||
let uv = mesh.GetTextureUV (i);
|
||||
if (!IsCorrectNumber (uv.x)) {
|
||||
return false;
|
||||
}
|
||||
if (!IsCorrectNumber (uv.y)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < mesh.TriangleCount (); i++) {
|
||||
let triangle = mesh.GetTriangle (i);
|
||||
if (!CheckTriangle (model, mesh, triangle)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
for (let i = 0; i < model.MeshCount (); i++) {
|
||||
let mesh = model.GetMesh (i);
|
||||
if (!CheckMesh (model, mesh)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
163
source/model/modelutils.js
Normal file
163
source/model/modelutils.js
Normal file
@ -0,0 +1,163 @@
|
||||
OV.CalculateTriangleNormal = function (v0, v1, v2)
|
||||
{
|
||||
let v = OV.SubCoord3D (v1, v0);
|
||||
let w = OV.SubCoord3D (v2, v0);
|
||||
|
||||
let normal = new OV.Coord3D (
|
||||
v.y * w.z - v.z * w.y,
|
||||
v.z * w.x - v.x * w.z,
|
||||
v.x * w.y - v.y * w.x
|
||||
);
|
||||
|
||||
normal.Normalize ();
|
||||
return normal;
|
||||
};
|
||||
|
||||
OV.TransformMesh = function (mesh, transformation)
|
||||
{
|
||||
if (transformation.IsIdentity ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < mesh.VertexCount (); i++) {
|
||||
let vertex = mesh.GetVertex (i);
|
||||
let transformed = transformation.TransformCoord3D (vertex);
|
||||
vertex.x = transformed.x;
|
||||
vertex.y = transformed.y;
|
||||
vertex.z = transformed.z;
|
||||
}
|
||||
|
||||
if (mesh.NormalCount () > 0) {
|
||||
let trs = transformation.GetMatrix ().DecomposeTRS ();
|
||||
let normalMatrix = new OV.Matrix ().ComposeTRS ([0.0, 0.0, 0.0], trs.rotation, [1.0, 1.0, 1.0]);
|
||||
let normalTransformation = new OV.Transformation (normalMatrix);
|
||||
for (let i = 0; i < mesh.NormalCount (); i++) {
|
||||
let normal = mesh.GetNormal (i);
|
||||
let transformed = normalTransformation.TransformCoord3D (normal);
|
||||
normal.x = transformed.x;
|
||||
normal.y = transformed.y;
|
||||
normal.z = transformed.z;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
OV.GetMeshBoundingBox = function (mesh)
|
||||
{
|
||||
let min = new OV.Coord3D (Infinity, Infinity, Infinity);
|
||||
let max = new OV.Coord3D (-Infinity, -Infinity, -Infinity);
|
||||
|
||||
for (let i = 0; i < mesh.VertexCount (); i++) {
|
||||
let vertex = mesh.GetVertex (i);
|
||||
min.x = Math.min (min.x, vertex.x);
|
||||
min.y = Math.min (min.y, vertex.y);
|
||||
min.z = Math.min (min.z, vertex.z);
|
||||
max.x = Math.max (max.x, vertex.x);
|
||||
max.y = Math.max (max.y, vertex.y);
|
||||
max.z = Math.max (max.z, vertex.z);
|
||||
}
|
||||
|
||||
return [min, max];
|
||||
};
|
||||
|
||||
OV.IsModelEmpty = function (model)
|
||||
{
|
||||
for (let i = 0; i < model.MeshCount (); i++) {
|
||||
let mesh = model.GetMesh (i);
|
||||
if (mesh.TriangleCount () > 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
OV.CloneMesh = function (mesh)
|
||||
{
|
||||
let cloned = new OV.Mesh ();
|
||||
|
||||
cloned.SetName (mesh.GetName ());
|
||||
|
||||
for (let i = 0; i < mesh.VertexCount (); i++) {
|
||||
let vertex = mesh.GetVertex (i);
|
||||
cloned.AddVertex (vertex.Clone ());
|
||||
}
|
||||
|
||||
for (let i = 0; i < mesh.NormalCount (); i++) {
|
||||
let normal = mesh.GetNormal (i);
|
||||
cloned.AddNormal (normal.Clone ());
|
||||
}
|
||||
|
||||
for (let i = 0; i < mesh.TextureUVCount (); i++) {
|
||||
let uv = mesh.GetTextureUV (i);
|
||||
cloned.AddTextureUV (uv.Clone ());
|
||||
}
|
||||
|
||||
for (let i = 0; i < mesh.TriangleCount (); i++) {
|
||||
let triangle = mesh.GetTriangle (i);
|
||||
cloned.AddTriangle (triangle.Clone ());
|
||||
}
|
||||
|
||||
return cloned;
|
||||
};
|
||||
|
||||
OV.CreateMergedModel = function (model)
|
||||
{
|
||||
function MergeMesh (mesh, mergedMesh)
|
||||
{
|
||||
let vertexOffset = mergedMesh.VertexCount ();
|
||||
let normalOffset = mergedMesh.NormalCount ();
|
||||
let uvOffset = mergedMesh.TextureUVCount ();
|
||||
|
||||
for (let i = 0; i < mesh.VertexCount (); i++) {
|
||||
let vertex = mesh.GetVertex (i);
|
||||
mergedMesh.AddVertex (vertex.Clone ());
|
||||
}
|
||||
for (let i = 0; i < mesh.NormalCount (); i++) {
|
||||
let normal = mesh.GetNormal (i);
|
||||
mergedMesh.AddNormal (normal.Clone ());
|
||||
}
|
||||
for (let i = 0; i < mesh.TextureUVCount (); i++) {
|
||||
let uv = mesh.GetTextureUV (i);
|
||||
mergedMesh.AddTextureUV (uv.Clone ());
|
||||
}
|
||||
|
||||
for (let i = 0; i < mesh.TriangleCount (); i++) {
|
||||
let triangle = mesh.GetTriangle (i);
|
||||
let newTriangle = triangle.Clone ();
|
||||
newTriangle.SetVertices (
|
||||
triangle.v0 + vertexOffset,
|
||||
triangle.v1 + vertexOffset,
|
||||
triangle.v2 + vertexOffset
|
||||
);
|
||||
newTriangle.SetNormals (
|
||||
triangle.n0 + normalOffset,
|
||||
triangle.n1 + normalOffset,
|
||||
triangle.n2 + normalOffset
|
||||
);
|
||||
if (newTriangle.HasTextureUVs ()) {
|
||||
newTriangle.SetTextureUVs (
|
||||
triangle.u0 + uvOffset,
|
||||
triangle.u1 + uvOffset,
|
||||
triangle.u2 + uvOffset
|
||||
);
|
||||
}
|
||||
mergedMesh.AddTriangle (newTriangle);
|
||||
}
|
||||
}
|
||||
|
||||
let mergedModel = new OV.Model ();
|
||||
mergedModel.SetName (model.GetName ());
|
||||
|
||||
for (let i = 0; i < model.MaterialCount (); i++) {
|
||||
let material = model.GetMaterial (i);
|
||||
mergedModel.AddMaterial (material.Clone ());
|
||||
}
|
||||
|
||||
let mergedMesh = new OV.Mesh ();
|
||||
for (let i = 0; i < model.MeshCount (); i++) {
|
||||
let mesh = model.GetMesh (i);
|
||||
MergeMesh (mesh, mergedMesh);
|
||||
}
|
||||
|
||||
mergedModel.AddMesh (mergedMesh);
|
||||
return mergedModel;
|
||||
};
|
||||
21
source/viewer/domutils.js
Normal file
21
source/viewer/domutils.js
Normal file
@ -0,0 +1,21 @@
|
||||
OV.GetInnerDimensions = function (element, outerWidth, outerHeight)
|
||||
{
|
||||
function GetInt (parameter)
|
||||
{
|
||||
return Math.round (parseFloat (parameter));
|
||||
}
|
||||
|
||||
let style = getComputedStyle (element);
|
||||
let width = outerWidth -
|
||||
GetInt (style.borderLeftWidth) - GetInt (style.borderRightWidth) -
|
||||
GetInt (style.marginLeft) - GetInt (style.marginRight) -
|
||||
GetInt (style.paddingLeft) - GetInt (style.paddingRight);
|
||||
let height = outerHeight -
|
||||
GetInt (style.borderTopWidth) - GetInt (style.borderBottomWidth) -
|
||||
GetInt (style.marginTop) - GetInt (style.marginBottom) -
|
||||
GetInt (style.paddingTop) - GetInt (style.paddingBottom);
|
||||
return {
|
||||
width : width,
|
||||
height : height
|
||||
};
|
||||
};
|
||||
83
source/viewer/domviewer.js
Normal file
83
source/viewer/domviewer.js
Normal file
@ -0,0 +1,83 @@
|
||||
OV.Init3DViewerElements = function ()
|
||||
{
|
||||
function LoadElement (element)
|
||||
{
|
||||
let canvas = document.createElement ('canvas');
|
||||
element.appendChild (canvas);
|
||||
|
||||
let viewer = new OV.Viewer ();
|
||||
viewer.Init (canvas);
|
||||
|
||||
let width = parseInt (element.getAttribute ('width'));
|
||||
let height = parseInt (element.getAttribute ('height'));
|
||||
element.style.width = width + 'px';
|
||||
element.style.height = height + 'px';
|
||||
viewer.Resize (width, height);
|
||||
|
||||
let loader = new OV.ThreeModelLoader ();
|
||||
let progressDiv = null;
|
||||
loader.Init ({
|
||||
onLoadStart : function () {
|
||||
canvas.style.display = 'none';
|
||||
progressDiv = document.createElement ('div');
|
||||
element.appendChild (progressDiv);
|
||||
progressDiv.innerHTML = 'Loading model...';
|
||||
},
|
||||
onFilesLoaded : function () {
|
||||
progressDiv.innerHTML = 'Importing model...';
|
||||
},
|
||||
onModelImported : function () {
|
||||
progressDiv.innerHTML = 'Visualizing model...';
|
||||
},
|
||||
onModelFinished : function (importResult, threeMeshes) {
|
||||
element.removeChild (progressDiv);
|
||||
canvas.style.display = 'initial';
|
||||
viewer.AddMeshes (threeMeshes);
|
||||
let boundingSphere = viewer.GetBoundingSphere (function (meshUserData) {
|
||||
return true;
|
||||
});
|
||||
viewer.AdjustClippingPlanes (boundingSphere);
|
||||
viewer.FitToWindow (boundingSphere, false);
|
||||
},
|
||||
onTextureLoaded : function () {
|
||||
viewer.Render ();
|
||||
},
|
||||
onLoadError : function (importerError) {
|
||||
progressDiv.innerHTML = 'Unknown error.';
|
||||
},
|
||||
});
|
||||
|
||||
let model = element.getAttribute ('model');
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
|
||||
let modelUrls = model.split (',');
|
||||
if (modelUrls.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let camera = element.getAttribute ('camera');
|
||||
if (camera) {
|
||||
let cameraParams = camera.split (',');
|
||||
if (cameraParams.length === 9) {
|
||||
let camera = new OV.Camera (
|
||||
new OV.Coord3D (parseFloat (cameraParams[0]), parseFloat (cameraParams[1]), parseFloat (cameraParams[2])),
|
||||
new OV.Coord3D (parseFloat (cameraParams[3]), parseFloat (cameraParams[4]), parseFloat (cameraParams[5])),
|
||||
new OV.Coord3D (parseFloat (cameraParams[6]), parseFloat (cameraParams[7]), parseFloat (cameraParams[8]))
|
||||
);
|
||||
viewer.SetCamera (camera);
|
||||
}
|
||||
}
|
||||
|
||||
loader.LoadFromUrlList (modelUrls);
|
||||
}
|
||||
|
||||
window.addEventListener ('load', function () {
|
||||
let elements = document.getElementsByClassName ('online_3d_viewer');
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let element = elements[i];
|
||||
LoadElement (element);
|
||||
}
|
||||
});
|
||||
};
|
||||
549
source/viewer/navigation.js
Normal file
549
source/viewer/navigation.js
Normal file
@ -0,0 +1,549 @@
|
||||
OV.GetClientCoordinates = function (canvas, clientX, clientY)
|
||||
{
|
||||
if (canvas.getBoundingClientRect) {
|
||||
let clientRect = canvas.getBoundingClientRect ();
|
||||
clientX -= clientRect.left;
|
||||
clientY -= clientRect.top;
|
||||
}
|
||||
if (window.pageXOffset && window.pageYOffset) {
|
||||
clientX += window.pageXOffset;
|
||||
clientY += window.pageYOffset;
|
||||
}
|
||||
return (new OV.Coord2D (clientX, clientY));
|
||||
};
|
||||
|
||||
OV.Camera = class
|
||||
{
|
||||
constructor (eye, center, up)
|
||||
{
|
||||
this.eye = eye;
|
||||
this.center = center;
|
||||
this.up = up;
|
||||
}
|
||||
|
||||
Clone ()
|
||||
{
|
||||
return new OV.Camera (
|
||||
this.eye.Clone (),
|
||||
this.center.Clone (),
|
||||
this.up.Clone ()
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
OV.MouseInteraction = class
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
this.prev = new OV.Coord2D (0.0, 0.0);
|
||||
this.curr = new OV.Coord2D (0.0, 0,0);
|
||||
this.diff = new OV.Coord2D (0.0, 0.0);
|
||||
this.buttons = [];
|
||||
}
|
||||
|
||||
Down (canvas, ev)
|
||||
{
|
||||
this.buttons.push (ev.which);
|
||||
this.curr = this.GetCurrent (canvas, ev);
|
||||
this.prev = this.curr.Clone ();
|
||||
}
|
||||
|
||||
Move (canvas, ev)
|
||||
{
|
||||
this.curr = this.GetCurrent (canvas, ev);
|
||||
this.diff = OV.SubCoord2D (this.curr, this.prev);
|
||||
this.prev = this.curr.Clone ();
|
||||
}
|
||||
|
||||
Up (canvas, ev)
|
||||
{
|
||||
let buttonIndex = this.buttons.indexOf (ev.which);
|
||||
if (buttonIndex !== -1) {
|
||||
this.buttons.splice (buttonIndex, 1);
|
||||
}
|
||||
this.curr = this.GetCurrent (canvas, ev);
|
||||
}
|
||||
|
||||
Leave (canvas, ev)
|
||||
{
|
||||
this.buttons = [];
|
||||
this.curr = this.GetCurrent (canvas, ev);
|
||||
}
|
||||
|
||||
IsButtonDown ()
|
||||
{
|
||||
return this.buttons.length > 0;
|
||||
}
|
||||
|
||||
GetButton ()
|
||||
{
|
||||
let length = this.buttons.length;
|
||||
if (length === 0) {
|
||||
return 0;
|
||||
}
|
||||
return this.buttons[length - 1];
|
||||
}
|
||||
|
||||
GetMoveDiff ()
|
||||
{
|
||||
return this.diff;
|
||||
}
|
||||
|
||||
GetCurrent (canvas, ev)
|
||||
{
|
||||
return OV.GetClientCoordinates (canvas, ev.clientX, ev.clientY);
|
||||
}
|
||||
};
|
||||
|
||||
OV.TouchInteraction = class
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
this.prevPos = new OV.Coord2D (0.0, 0.0);
|
||||
this.currPos = new OV.Coord2D (0.0, 0,0);
|
||||
this.diffPos = new OV.Coord2D (0.0, 0.0);
|
||||
this.prevDist = 0.0;
|
||||
this.currDist = 0.0;
|
||||
this.diffDist = 0.0;
|
||||
this.fingers = 0;
|
||||
}
|
||||
|
||||
Start (canvas, ev)
|
||||
{
|
||||
if (ev.touches.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.fingers = ev.touches.length;
|
||||
|
||||
this.currPos = this.GetCurrent (canvas, ev);
|
||||
this.prevPos = this.currPos.Clone ();
|
||||
|
||||
this.currDist = this.GetTouchDistance (canvas, ev);
|
||||
this.prevDist = this.currDist;
|
||||
}
|
||||
|
||||
Move (canvas, ev)
|
||||
{
|
||||
if (ev.touches.length === 0) {
|
||||
return;
|
||||
}
|
||||
this.currPos = this.GetCurrent (canvas, ev);
|
||||
this.diffPos = OV.SubCoord2D (this.currPos, this.prevPos);
|
||||
this.prevPos = this.currPos.Clone ();
|
||||
|
||||
this.currDist = this.GetTouchDistance (canvas, ev);
|
||||
this.diffDist = this.currDist - this.prevDist;
|
||||
this.prevDist = this.currDist;
|
||||
}
|
||||
|
||||
End (canvas, ev)
|
||||
{
|
||||
if (ev.touches.length === 0) {
|
||||
return;
|
||||
}
|
||||
this.fingers = 0;
|
||||
this.currPos = this.GetCurrent (canvas, ev);
|
||||
this.currDist = this.GetTouchDistance (canvas, ev);
|
||||
}
|
||||
|
||||
IsFingerDown ()
|
||||
{
|
||||
return this.fingers !== 0;
|
||||
}
|
||||
|
||||
GetFingerCount ()
|
||||
{
|
||||
return this.fingers;
|
||||
}
|
||||
|
||||
GetMoveDiff ()
|
||||
{
|
||||
return this.diffPos;
|
||||
}
|
||||
|
||||
GetDistanceDiff ()
|
||||
{
|
||||
return this.diffDist;
|
||||
}
|
||||
|
||||
GetCurrent (canvas, ev)
|
||||
{
|
||||
let coord = null;
|
||||
if (ev.touches.length !== 0) {
|
||||
let touchEv = ev.touches[0];
|
||||
coord = OV.GetClientCoordinates (canvas, touchEv.pageX, touchEv.pageY);
|
||||
}
|
||||
return coord;
|
||||
}
|
||||
|
||||
GetTouchDistance (canvas, ev)
|
||||
{
|
||||
if (ev.touches.length !== 2) {
|
||||
return 0.0;
|
||||
}
|
||||
let touchEv1 = ev.touches[0];
|
||||
let touchEv2 = ev.touches[1];
|
||||
let distance = OV.CoordDistance2D (
|
||||
OV.GetClientCoordinates (canvas, touchEv1.pageX, touchEv1.pageY),
|
||||
OV.GetClientCoordinates (canvas, touchEv2.pageX, touchEv2.pageY)
|
||||
);
|
||||
return distance;
|
||||
}
|
||||
};
|
||||
|
||||
OV.ClickDetector = class
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
this.isClick = false;
|
||||
this.button = 0;
|
||||
}
|
||||
|
||||
Down (ev)
|
||||
{
|
||||
this.isClick = true;
|
||||
this.button = ev.which;
|
||||
}
|
||||
|
||||
Move ()
|
||||
{
|
||||
this.isClick = false;
|
||||
}
|
||||
|
||||
Up (ev)
|
||||
{
|
||||
if (this.isClick && ev.which !== this.button) {
|
||||
this.isClick = false;
|
||||
}
|
||||
}
|
||||
|
||||
Leave (ev)
|
||||
{
|
||||
this.isClick = false;
|
||||
this.button = 0;
|
||||
}
|
||||
|
||||
IsClick ()
|
||||
{
|
||||
return this.isClick;
|
||||
}
|
||||
};
|
||||
|
||||
OV.Navigation = class
|
||||
{
|
||||
constructor (canvas, camera)
|
||||
{
|
||||
this.canvas = canvas;
|
||||
this.camera = camera;
|
||||
this.orbitCenter = this.camera.center.Clone ();
|
||||
this.fixUpVector = true;
|
||||
|
||||
this.mouse = new OV.MouseInteraction ();
|
||||
this.touch = new OV.TouchInteraction ();
|
||||
this.clickDetector = new OV.ClickDetector ();
|
||||
|
||||
this.onUpdate = null;
|
||||
this.onClick = null;
|
||||
|
||||
if (this.canvas.addEventListener) {
|
||||
this.canvas.addEventListener ('mousedown', this.OnMouseDown.bind (this));
|
||||
this.canvas.addEventListener ('DOMMouseScroll', this.OnMouseWheel.bind (this));
|
||||
this.canvas.addEventListener ('mousewheel', this.OnMouseWheel.bind (this));
|
||||
this.canvas.addEventListener ('touchstart', this.OnTouchStart.bind (this));
|
||||
this.canvas.addEventListener ('touchmove', this.OnTouchMove.bind (this));
|
||||
this.canvas.addEventListener ('touchend', this.OnTouchEnd.bind (this));
|
||||
this.canvas.addEventListener ('contextmenu', this.OnContextMenu.bind (this));
|
||||
}
|
||||
if (document.addEventListener) {
|
||||
document.addEventListener ('mousemove', this.OnMouseMove.bind (this));
|
||||
document.addEventListener ('mouseup', this.OnMouseUp.bind (this));
|
||||
document.addEventListener ('mouseleave', this.OnMouseLeave.bind (this));
|
||||
}
|
||||
}
|
||||
|
||||
SetUpdateHandler (onUpdate)
|
||||
{
|
||||
this.onUpdate = onUpdate;
|
||||
}
|
||||
|
||||
SetClickHandler (onClick)
|
||||
{
|
||||
this.onClick = onClick;
|
||||
}
|
||||
|
||||
IsFixUpVector ()
|
||||
{
|
||||
return this.fixUpVector;
|
||||
}
|
||||
|
||||
SetFixUpVector (fixUpVector)
|
||||
{
|
||||
this.fixUpVector = fixUpVector;
|
||||
}
|
||||
|
||||
GetCamera ()
|
||||
{
|
||||
return this.camera;
|
||||
}
|
||||
|
||||
SetCamera (camera)
|
||||
{
|
||||
this.camera = camera;
|
||||
}
|
||||
|
||||
MoveCamera (newCamera, stepCount)
|
||||
{
|
||||
function Step (obj, steps, count, index)
|
||||
{
|
||||
obj.camera.eye = steps.eye[index];
|
||||
obj.camera.center = steps.center[index];
|
||||
obj.camera.up = steps.up[index];
|
||||
obj.Update ();
|
||||
|
||||
if (index < count - 1) {
|
||||
requestAnimationFrame (function () {
|
||||
Step (obj, steps, count, index + 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (stepCount === 0) {
|
||||
this.SetCamera (newCamera);
|
||||
return;
|
||||
}
|
||||
|
||||
if (OV.CoordIsEqual3D (this.camera.eye, newCamera.eye) &&
|
||||
OV.CoordIsEqual3D (this.camera.center, newCamera.center) &&
|
||||
OV.CoordIsEqual3D (this.camera.up, newCamera.up))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let tweenFunc = OV.ParabolicTweenFunction;
|
||||
let obj = this;
|
||||
let steps = {
|
||||
eye : OV.TweenCoord3D (this.camera.eye, newCamera.eye, stepCount, tweenFunc),
|
||||
center : OV.TweenCoord3D (this.camera.center, newCamera.center, stepCount, tweenFunc),
|
||||
up : OV.TweenCoord3D (this.camera.up, newCamera.up, stepCount, tweenFunc)
|
||||
};
|
||||
requestAnimationFrame (function () {
|
||||
Step (obj, steps, stepCount, 0);
|
||||
});
|
||||
this.Update ();
|
||||
}
|
||||
|
||||
FitToSphere (center, radius, fov)
|
||||
{
|
||||
if (OV.IsZero (radius)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let fitCamera = this.GetFitToSphereCamera (center, radius, fov);
|
||||
this.camera = fitCamera;
|
||||
|
||||
this.orbitCenter = this.camera.center.Clone ();
|
||||
this.Update ();
|
||||
}
|
||||
|
||||
GetFitToSphereCamera (center, radius, fov)
|
||||
{
|
||||
if (OV.IsZero (radius)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let fitCamera = this.camera.Clone ();
|
||||
|
||||
let offsetToOrigo = OV.SubCoord3D (fitCamera.center, center);
|
||||
fitCamera.eye = OV.SubCoord3D (fitCamera.eye, offsetToOrigo);
|
||||
fitCamera.center = center.Clone ();
|
||||
|
||||
let centerEyeDirection = OV.SubCoord3D (fitCamera.eye, fitCamera.center).Normalize ();
|
||||
let fieldOfView = fov / 2.0;
|
||||
if (this.canvas.width < this.canvas.height) {
|
||||
fieldOfView = fieldOfView * this.canvas.width / this.canvas.height;
|
||||
}
|
||||
let distance = radius / Math.sin (fieldOfView * OV.DegRad);
|
||||
|
||||
fitCamera.eye = fitCamera.center.Clone ().Offset (centerEyeDirection, distance);
|
||||
this.orbitCenter = fitCamera.center.Clone ();
|
||||
return fitCamera;
|
||||
}
|
||||
|
||||
OnMouseDown (ev)
|
||||
{
|
||||
ev.preventDefault ();
|
||||
this.mouse.Down (this.canvas, ev);
|
||||
this.clickDetector.Down (ev);
|
||||
}
|
||||
|
||||
OnMouseMove (ev)
|
||||
{
|
||||
this.mouse.Move (this.canvas, ev);
|
||||
this.clickDetector.Move ();
|
||||
if (!this.mouse.IsButtonDown ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let moveDiff = this.mouse.GetMoveDiff ();
|
||||
let mouseButton = this.mouse.GetButton ();
|
||||
if (mouseButton === 1) {
|
||||
let orbitRatio = 0.5;
|
||||
this.Orbit (moveDiff.x * orbitRatio, moveDiff.y * orbitRatio);
|
||||
} else if (mouseButton === 3) {
|
||||
let eyeCenterDistance = OV.CoordDistance3D (this.camera.eye, this.camera.center);
|
||||
let panRatio = 0.001 * eyeCenterDistance;
|
||||
this.Pan (moveDiff.x * panRatio, moveDiff.y * panRatio);
|
||||
}
|
||||
|
||||
this.Update ();
|
||||
}
|
||||
|
||||
OnMouseUp (ev)
|
||||
{
|
||||
this.mouse.Up (this.canvas, ev);
|
||||
this.clickDetector.Up (ev);
|
||||
if (this.clickDetector.IsClick ()) {
|
||||
let isCtrlPressed = (ev.ctrlKey || ev.metaKey);
|
||||
this.Click (ev.which, isCtrlPressed, ev.clientX, ev.clientY);
|
||||
}
|
||||
}
|
||||
|
||||
OnMouseLeave (ev)
|
||||
{
|
||||
this.mouse.Leave (this.canvas, ev);
|
||||
this.clickDetector.Leave (ev);
|
||||
}
|
||||
|
||||
OnTouchStart (ev)
|
||||
{
|
||||
ev.preventDefault ();
|
||||
this.touch.Start (this.canvas, ev);
|
||||
}
|
||||
|
||||
OnTouchMove (ev)
|
||||
{
|
||||
ev.preventDefault ();
|
||||
this.touch.Move (this.canvas, ev);
|
||||
if (!this.touch.IsFingerDown ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let moveDiff = this.touch.GetMoveDiff ();
|
||||
let distanceDiff = this.touch.GetDistanceDiff ();
|
||||
let fingerCount = this.touch.GetFingerCount ();
|
||||
if (fingerCount === 1) {
|
||||
let orbitRatio = 0.5;
|
||||
this.Orbit (moveDiff.x * orbitRatio, moveDiff.y * orbitRatio);
|
||||
} else if (fingerCount === 2) {
|
||||
let zoomRatio = 0.005;
|
||||
this.Zoom (distanceDiff * zoomRatio);
|
||||
let panRatio = 0.001 * OV.CoordDistance3D (this.camera.eye, this.camera.center);
|
||||
this.Pan (moveDiff.x * panRatio, moveDiff.y * panRatio);
|
||||
}
|
||||
|
||||
this.Update ();
|
||||
}
|
||||
|
||||
OnTouchEnd (ev)
|
||||
{
|
||||
ev.preventDefault ();
|
||||
this.touch.End (this.canvas, ev);
|
||||
}
|
||||
|
||||
OnMouseWheel (ev)
|
||||
{
|
||||
ev.preventDefault ();
|
||||
let params = ev || window.event;
|
||||
|
||||
let delta = 0;
|
||||
if (params.detail) {
|
||||
delta = -params.detail;
|
||||
} else if (params.wheelDelta) {
|
||||
delta = params.wheelDelta / 40;
|
||||
}
|
||||
|
||||
let ratio = 0.1;
|
||||
if (delta < 0) {
|
||||
ratio = ratio * -1.0;
|
||||
}
|
||||
|
||||
this.Zoom (ratio);
|
||||
this.Update ();
|
||||
}
|
||||
|
||||
OnContextMenu (ev)
|
||||
{
|
||||
ev.preventDefault ();
|
||||
}
|
||||
|
||||
Orbit (angleX, angleY)
|
||||
{
|
||||
let radAngleX = angleX * OV.DegRad;
|
||||
let radAngleY = angleY * OV.DegRad;
|
||||
|
||||
let viewDirection = OV.SubCoord3D (this.camera.center, this.camera.eye).Normalize ();
|
||||
let horizontalDirection = OV.CrossVector3D (viewDirection, this.camera.up).Normalize ();
|
||||
let differentCenter = !OV.CoordIsEqual3D (this.orbitCenter, this.camera.center);
|
||||
|
||||
if (this.fixUpVector) {
|
||||
let originalAngle = OV.VectorAngle3D (viewDirection, this.camera.up);
|
||||
let newAngle = originalAngle + radAngleY;
|
||||
if (OV.IsGreater (newAngle, 0.0) && OV.IsLower (newAngle, Math.PI)) {
|
||||
this.camera.eye.Rotate (horizontalDirection, -radAngleY, this.orbitCenter);
|
||||
if (differentCenter) {
|
||||
this.camera.center.Rotate (horizontalDirection, -radAngleY, this.orbitCenter);
|
||||
}
|
||||
}
|
||||
this.camera.eye.Rotate (this.camera.up, -radAngleX, this.orbitCenter);
|
||||
if (differentCenter) {
|
||||
this.camera.center.Rotate (this.camera.up, -radAngleX, this.orbitCenter);
|
||||
}
|
||||
} else {
|
||||
let verticalDirection = OV.CrossVector3D (horizontalDirection, viewDirection).Normalize ();
|
||||
this.camera.eye.Rotate (horizontalDirection, -radAngleY, this.orbitCenter);
|
||||
this.camera.eye.Rotate (verticalDirection, -radAngleX, this.orbitCenter);
|
||||
if (differentCenter) {
|
||||
this.camera.center.Rotate (horizontalDirection, -radAngleY, this.orbitCenter);
|
||||
this.camera.center.Rotate (verticalDirection, -radAngleX, this.orbitCenter);
|
||||
}
|
||||
this.camera.up = verticalDirection;
|
||||
}
|
||||
}
|
||||
|
||||
Pan (moveX, moveY)
|
||||
{
|
||||
let viewDirection = OV.SubCoord3D (this.camera.center, this.camera.eye).Normalize ();
|
||||
let horizontalDirection = OV.CrossVector3D (viewDirection, this.camera.up).Normalize ();
|
||||
let verticalDirection = OV.CrossVector3D (horizontalDirection, viewDirection).Normalize ();
|
||||
|
||||
this.camera.eye.Offset (horizontalDirection, -moveX);
|
||||
this.camera.center.Offset (horizontalDirection, -moveX);
|
||||
|
||||
this.camera.eye.Offset (verticalDirection, moveY);
|
||||
this.camera.center.Offset (verticalDirection, moveY);
|
||||
}
|
||||
|
||||
Zoom (ratio)
|
||||
{
|
||||
let direction = OV.SubCoord3D (this.camera.center, this.camera.eye);
|
||||
let distance = direction.Length ();
|
||||
let move = distance * ratio;
|
||||
this.camera.eye.Offset (direction, move);
|
||||
}
|
||||
|
||||
Update ()
|
||||
{
|
||||
if (this.onUpdate) {
|
||||
this.onUpdate ();
|
||||
}
|
||||
}
|
||||
|
||||
Click (button, isCtrlPressed, clientX, clientY)
|
||||
{
|
||||
if (this.onClick) {
|
||||
let mouseCoords = OV.GetClientCoordinates (this.canvas, clientX, clientY);
|
||||
this.onClick (button, isCtrlPressed, mouseCoords);
|
||||
}
|
||||
}
|
||||
};
|
||||
375
source/viewer/viewer.js
Normal file
375
source/viewer/viewer.js
Normal file
@ -0,0 +1,375 @@
|
||||
OV.GetDefaultCamera = function (direction)
|
||||
{
|
||||
if (direction === OV.Direction.X) {
|
||||
return new OV.Camera (
|
||||
new OV.Coord3D (2.0, -3.0, 1.5),
|
||||
new OV.Coord3D (0.0, 0.0, 0.0),
|
||||
new OV.Coord3D (1.0, 0.0, 0.0)
|
||||
);
|
||||
} else if (direction === OV.Direction.Y) {
|
||||
return new OV.Camera (
|
||||
new OV.Coord3D (-1.5, 2.0, 3.0),
|
||||
new OV.Coord3D (0.0, 0.0, 0.0),
|
||||
new OV.Coord3D (0.0, 1.0, 0.0)
|
||||
);
|
||||
} else if (direction === OV.Direction.Z) {
|
||||
return new OV.Camera (
|
||||
new OV.Coord3D (-1.5, -3.0, 2.0),
|
||||
new OV.Coord3D (0.0, 0.0, 0.0),
|
||||
new OV.Coord3D (0.0, 0.0, 1.0)
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
OV.UpVector = class
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
this.direction = OV.Direction.Z;
|
||||
this.isFixed = true;
|
||||
this.isFlipped = false;
|
||||
}
|
||||
|
||||
SetDirection (newDirection, oldCamera)
|
||||
{
|
||||
this.direction = newDirection;
|
||||
this.isFlipped = false;
|
||||
|
||||
let defaultCamera = OV.GetDefaultCamera (this.direction);
|
||||
let defaultDir = OV.SubCoord3D (defaultCamera.eye, defaultCamera.center);
|
||||
|
||||
let distance = OV.CoordDistance3D (oldCamera.center, oldCamera.eye);
|
||||
let newEye = oldCamera.center.Clone ().Offset (defaultDir, distance);
|
||||
|
||||
let newCamera = oldCamera.Clone ();
|
||||
if (this.direction === OV.Direction.Y) {
|
||||
newCamera.up = new OV.Coord3D (0.0, 1.0, 0.0);
|
||||
newCamera.eye = newEye;
|
||||
} else if (this.direction === OV.Direction.Z) {
|
||||
newCamera.up = new OV.Coord3D (0.0, 0.0, 1.0);
|
||||
newCamera.eye = newEye;
|
||||
}
|
||||
return newCamera;
|
||||
}
|
||||
|
||||
SetFixed (isFixed, oldCamera)
|
||||
{
|
||||
this.isFixed = isFixed;
|
||||
if (this.isFixed) {
|
||||
return this.SetDirection (this.direction, oldCamera);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Flip (oldCamera)
|
||||
{
|
||||
this.isFlipped = !this.isFlipped;
|
||||
let newCamera = oldCamera.Clone ();
|
||||
newCamera.up.MultiplyScalar (-1.0);
|
||||
return newCamera;
|
||||
}
|
||||
};
|
||||
|
||||
OV.Viewer = class
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
this.canvas = null;
|
||||
this.renderer = null;
|
||||
this.scene = null;
|
||||
this.camera = null;
|
||||
this.light = null;
|
||||
this.navigation = null;
|
||||
this.upVector = null;
|
||||
this.settings = {
|
||||
animationSteps : 40
|
||||
};
|
||||
}
|
||||
|
||||
Init (canvas)
|
||||
{
|
||||
this.canvas = canvas;
|
||||
this.canvas.id = 'viewer';
|
||||
this.canvas.addEventListener ('contenxtmenu', function (ev) {
|
||||
ev.preventDefault ();
|
||||
});
|
||||
|
||||
let parameters = {
|
||||
canvas : this.canvas,
|
||||
antialias : true
|
||||
};
|
||||
|
||||
this.renderer = new THREE.WebGLRenderer (parameters);
|
||||
this.renderer.setClearColor ('#ffffff', 1);
|
||||
this.renderer.setSize (this.canvas.width, this.canvas.height);
|
||||
|
||||
this.scene = new THREE.Scene ();
|
||||
|
||||
this.InitCamera ();
|
||||
this.InitLights ();
|
||||
|
||||
this.Render ();
|
||||
}
|
||||
|
||||
SetClickHandler (onClick)
|
||||
{
|
||||
this.navigation.SetClickHandler (onClick);
|
||||
}
|
||||
|
||||
GetCamera ()
|
||||
{
|
||||
return this.navigation.GetCamera ();
|
||||
}
|
||||
|
||||
SetCamera (camera)
|
||||
{
|
||||
this.navigation.SetCamera (camera);
|
||||
this.Render ();
|
||||
}
|
||||
|
||||
Resize (width, height)
|
||||
{
|
||||
let innerSize = OV.GetInnerDimensions (this.canvas, width, height);
|
||||
this.camera.aspect = innerSize.width / innerSize.height;
|
||||
this.camera.updateProjectionMatrix ();
|
||||
this.renderer.setSize (innerSize.width, innerSize.height);
|
||||
this.Render ();
|
||||
}
|
||||
|
||||
FitToWindow (boundingSphere, animation)
|
||||
{
|
||||
if (boundingSphere === null) {
|
||||
return;
|
||||
}
|
||||
let center = new OV.Coord3D (boundingSphere.center.x, boundingSphere.center.y, boundingSphere.center.z);
|
||||
let radius = boundingSphere.radius;
|
||||
let fov = this.camera.fov;
|
||||
if (animation) {
|
||||
let newCamera = this.navigation.GetFitToSphereCamera (center, radius, fov);
|
||||
this.navigation.MoveCamera (newCamera, this.settings.animationSteps);
|
||||
} else {
|
||||
this.navigation.FitToSphere (center, radius, fov);
|
||||
}
|
||||
}
|
||||
|
||||
AdjustClippingPlanes (boundingSphere)
|
||||
{
|
||||
if (boundingSphere === null) {
|
||||
return;
|
||||
}
|
||||
if (boundingSphere.radius < 10.0) {
|
||||
this.camera.near = 0.01;
|
||||
this.camera.far = 100.0;
|
||||
} else if (boundingSphere.radius < 100.0) {
|
||||
this.camera.near = 0.1;
|
||||
this.camera.far = 1000.0;
|
||||
} else if (boundingSphere.radius < 1000.0) {
|
||||
this.camera.near = 10.0;
|
||||
this.camera.far = 10000.0;
|
||||
} else {
|
||||
this.camera.near = 100.0;
|
||||
this.camera.far = 1000000.0;
|
||||
}
|
||||
this.camera.updateProjectionMatrix ();
|
||||
this.Render ();
|
||||
}
|
||||
|
||||
IsFixUpVector ()
|
||||
{
|
||||
return this.navigation.IsFixUpVector ();
|
||||
}
|
||||
|
||||
SetFixUpVector (fixUpVector)
|
||||
{
|
||||
let oldCamera = this.navigation.GetCamera ();
|
||||
let newCamera = this.upVector.SetFixed (fixUpVector, oldCamera);
|
||||
this.navigation.SetFixUpVector (fixUpVector);
|
||||
if (newCamera !== null) {
|
||||
this.navigation.MoveCamera (newCamera, this.settings.animationSteps);
|
||||
}
|
||||
this.Render ();
|
||||
}
|
||||
|
||||
SetUpVector (upDirection, animate)
|
||||
{
|
||||
let oldCamera = this.navigation.GetCamera ();
|
||||
let newCamera = this.upVector.SetDirection (upDirection, oldCamera);
|
||||
let animationSteps = animate ? this.settings.animationSteps : 0;
|
||||
this.navigation.MoveCamera (newCamera, animationSteps);
|
||||
this.Render ();
|
||||
}
|
||||
|
||||
FlipUpVector ()
|
||||
{
|
||||
let oldCamera = this.navigation.GetCamera ();
|
||||
let newCamera = this.upVector.Flip (oldCamera);
|
||||
this.navigation.MoveCamera (newCamera, 0);
|
||||
this.Render ();
|
||||
}
|
||||
|
||||
Render ()
|
||||
{
|
||||
let navigationCamera = this.navigation.GetCamera ();
|
||||
this.camera.position.set (navigationCamera.eye.x, navigationCamera.eye.y, navigationCamera.eye.z);
|
||||
this.camera.up.set (navigationCamera.up.x, navigationCamera.up.y, navigationCamera.up.z);
|
||||
this.camera.lookAt (new THREE.Vector3 (navigationCamera.center.x, navigationCamera.center.y, navigationCamera.center.z));
|
||||
|
||||
let lightDir = OV.SubCoord3D (navigationCamera.eye, navigationCamera.center);
|
||||
this.light.position.set (lightDir.x, lightDir.y, lightDir.z);
|
||||
this.renderer.render (this.scene, this.camera);
|
||||
}
|
||||
|
||||
AddMeshes (meshes)
|
||||
{
|
||||
for (let i = 0; i < meshes.length; i++) {
|
||||
let mesh = meshes[i];
|
||||
this.scene.add (mesh);
|
||||
}
|
||||
this.Render ();
|
||||
}
|
||||
|
||||
Clear ()
|
||||
{
|
||||
this.ClearMeshes ();
|
||||
this.Render ();
|
||||
}
|
||||
|
||||
SetMeshesVisibility (isVisible)
|
||||
{
|
||||
this.EnumerateMeshes (function (mesh) {
|
||||
let visible = isVisible (mesh.userData);
|
||||
if (mesh.visible !== visible) {
|
||||
mesh.visible = visible;
|
||||
}
|
||||
});
|
||||
this.Render ();
|
||||
}
|
||||
|
||||
SetMeshesHighlight (highlightMaterial, isHighlighted)
|
||||
{
|
||||
function CreateHighlightMaterials (originalMaterials, highlightMaterial)
|
||||
{
|
||||
let highlightMaterials = [];
|
||||
for (let i = 0; i < originalMaterials.length; i++) {
|
||||
highlightMaterials.push (highlightMaterial);
|
||||
}
|
||||
return highlightMaterials;
|
||||
}
|
||||
|
||||
this.EnumerateMeshes (function (mesh) {
|
||||
let highlighted = isHighlighted (mesh.userData);
|
||||
if (highlighted) {
|
||||
if (mesh.userData.threeMaterials === null) {
|
||||
mesh.userData.threeMaterials = mesh.material;
|
||||
mesh.material = CreateHighlightMaterials (mesh.material, highlightMaterial);
|
||||
}
|
||||
} else {
|
||||
if (mesh.userData.threeMaterials !== null) {
|
||||
mesh.material = mesh.userData.threeMaterials;
|
||||
mesh.userData.threeMaterials = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
this.Render ();
|
||||
}
|
||||
|
||||
GetMeshUnderMouse (mouseCoords)
|
||||
{
|
||||
let raycaster = new THREE.Raycaster ();
|
||||
let mousePos = new THREE.Vector2 ();
|
||||
mousePos.x = (mouseCoords.x / this.canvas.width) * 2 - 1;
|
||||
mousePos.y = -(mouseCoords.y / this.canvas.height) * 2 + 1;
|
||||
raycaster.setFromCamera (mousePos, this.camera);
|
||||
let iSectObjects = raycaster.intersectObjects (this.scene.children);
|
||||
for (let i = 0; i < iSectObjects.length; i++) {
|
||||
let iSectObject = iSectObjects[i];
|
||||
if (iSectObject.object.type === 'Mesh' && iSectObject.object.visible) {
|
||||
return iSectObject.object.userData;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
GetBoundingBox (needToProcess)
|
||||
{
|
||||
let hasMesh = false;
|
||||
let boundingBox = new THREE.Box3 ();
|
||||
this.EnumerateMeshes (function (mesh) {
|
||||
if (needToProcess (mesh.userData)) {
|
||||
boundingBox.union (new THREE.Box3 ().setFromObject (mesh));
|
||||
hasMesh = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (!hasMesh) {
|
||||
return null;
|
||||
}
|
||||
return boundingBox;
|
||||
}
|
||||
|
||||
GetBoundingSphere (needToProcess)
|
||||
{
|
||||
let boundingBox = this.GetBoundingBox (needToProcess);
|
||||
if (boundingBox === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let boundingSphere = new THREE.Sphere ();
|
||||
boundingBox.getBoundingSphere (boundingSphere);
|
||||
return boundingSphere;
|
||||
}
|
||||
|
||||
EnumerateMeshesUserData (enumerator)
|
||||
{
|
||||
this.EnumerateMeshes (function (mesh) {
|
||||
enumerator (mesh.userData);
|
||||
});
|
||||
}
|
||||
|
||||
EnumerateMeshes (enumerator)
|
||||
{
|
||||
this.scene.traverse (function (object) {
|
||||
if (object.isMesh) {
|
||||
enumerator (object);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ClearMeshes ()
|
||||
{
|
||||
for (let i = this.scene.children.length - 1; i >= 0; i--) {
|
||||
let object = this.scene.children[i];
|
||||
if (object.isMesh) {
|
||||
object.geometry.dispose ();
|
||||
this.scene.remove (object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InitCamera ()
|
||||
{
|
||||
this.camera = new THREE.PerspectiveCamera (45.0, this.canvas.width / this.canvas.height, 0.1, 1000.0);
|
||||
this.scene.add (this.camera);
|
||||
|
||||
let canvasElem = this.renderer.domElement;
|
||||
let camera = OV.GetDefaultCamera (OV.Direction.Z);
|
||||
|
||||
let obj = this;
|
||||
this.navigation = new OV.Navigation (canvasElem, camera);
|
||||
this.navigation.SetUpdateHandler (function () {
|
||||
obj.Render ();
|
||||
});
|
||||
|
||||
this.upVector = new OV.UpVector ();
|
||||
}
|
||||
|
||||
InitLights ()
|
||||
{
|
||||
let ambientLight = new THREE.AmbientLight (0x888888);
|
||||
this.scene.add (ambientLight);
|
||||
|
||||
this.light = new THREE.DirectionalLight (0x888888);
|
||||
this.scene.add (this.light);
|
||||
}
|
||||
};
|
||||
14
test/framework/native_import.js
Normal file
14
test/framework/native_import.js
Normal file
@ -0,0 +1,14 @@
|
||||
module.exports =
|
||||
{
|
||||
AddNativeSourceFile : function (fileName)
|
||||
{
|
||||
var path = require ('path');
|
||||
var rootDir = path.join (__dirname, '..');
|
||||
var fullPath = path.resolve (rootDir, fileName);
|
||||
|
||||
var fs = require ('fs');
|
||||
var content = fs.readFileSync (fullPath).toString ();
|
||||
|
||||
eval.apply (global, [content]);
|
||||
}
|
||||
}
|
||||
8
test/package.json
Normal file
8
test/package.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"mocha": "*"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha"
|
||||
}
|
||||
}
|
||||
27
test/test.js
Normal file
27
test/test.js
Normal file
@ -0,0 +1,27 @@
|
||||
var fs = require ('fs');
|
||||
process.chdir (__dirname);
|
||||
|
||||
global.atob = function (base64String) {
|
||||
return Buffer.from (base64String, 'base64').toString('binary')
|
||||
};
|
||||
|
||||
global.Blob = function () {
|
||||
|
||||
};
|
||||
|
||||
global.URL = {
|
||||
createObjectURL : function () {
|
||||
return 'ObjectUrl';
|
||||
}
|
||||
}
|
||||
|
||||
require ('./utils/importall.js');
|
||||
var testDirName = './tests/';
|
||||
var files = fs.readdirSync (testDirName, { withFileTypes: true });
|
||||
var i, file;
|
||||
for (i = 0; i < files.length; i++) {
|
||||
file = files[i];
|
||||
if (file.isFile ()) {
|
||||
require (testDirName + file.name);
|
||||
}
|
||||
}
|
||||
BIN
test/testfiles/3ds/cube_four_instances.3ds
Normal file
BIN
test/testfiles/3ds/cube_four_instances.3ds
Normal file
Binary file not shown.
BIN
test/testfiles/3ds/cube_instance_scale_rotation.3ds
Normal file
BIN
test/testfiles/3ds/cube_instance_scale_rotation.3ds
Normal file
Binary file not shown.
BIN
test/testfiles/3ds/cube_two_instances.3ds
Normal file
BIN
test/testfiles/3ds/cube_two_instances.3ds
Normal file
Binary file not shown.
BIN
test/testfiles/3ds/cube_with_materials.3ds
Normal file
BIN
test/testfiles/3ds/cube_with_materials.3ds
Normal file
Binary file not shown.
BIN
test/testfiles/3ds/cube_with_texture_transformations.3ds
Normal file
BIN
test/testfiles/3ds/cube_with_texture_transformations.3ds
Normal file
Binary file not shown.
BIN
test/testfiles/3ds/texture.png
Normal file
BIN
test/testfiles/3ds/texture.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
BIN
test/testfiles/bin/binary_content.bin
Normal file
BIN
test/testfiles/bin/binary_content.bin
Normal file
Binary file not shown.
46
test/testfiles/bin/binary_content.txt
Normal file
46
test/testfiles/bin/binary_content.txt
Normal file
@ -0,0 +1,46 @@
|
||||
1 (bool, 1)
|
||||
0 (bool, 1)
|
||||
a (char, 1)
|
||||
A (char, 1)
|
||||
a (unsigned char, 1)
|
||||
A (unsigned char, 1)
|
||||
42 (short, 2)
|
||||
-42 (short, 2)
|
||||
32000 (short, 2)
|
||||
-32000 (short, 2)
|
||||
42 (unsigned short, 2)
|
||||
65494 (unsigned short, 2)
|
||||
32000 (unsigned short, 2)
|
||||
33536 (unsigned short, 2)
|
||||
42 (int, 4)
|
||||
-42 (int, 4)
|
||||
32000 (int, 4)
|
||||
-32000 (int, 4)
|
||||
2000000000 (int, 4)
|
||||
-2000000000 (int, 4)
|
||||
42 (unsigned int, 4)
|
||||
4294967254 (unsigned int, 4)
|
||||
32000 (unsigned int, 4)
|
||||
4294935296 (unsigned int, 4)
|
||||
2000000000 (unsigned int, 4)
|
||||
2294967296 (unsigned int, 4)
|
||||
42 (long, 4)
|
||||
-42 (long, 4)
|
||||
32000 (long, 4)
|
||||
-32000 (long, 4)
|
||||
2000000000 (long, 4)
|
||||
-2000000000 (long, 4)
|
||||
42 (unsigned long, 4)
|
||||
4294967254 (unsigned long, 4)
|
||||
32000 (unsigned long, 4)
|
||||
4294935296 (unsigned long, 4)
|
||||
2000000000 (unsigned long, 4)
|
||||
2294967296 (unsigned long, 4)
|
||||
42 (float, 4)
|
||||
-42 (float, 4)
|
||||
12345.7 (float, 4)
|
||||
-12345.7 (float, 4)
|
||||
42 (double, 8)
|
||||
-42 (double, 8)
|
||||
12345.7 (double, 8)
|
||||
-12345.7 (double, 8)
|
||||
105
test/testfiles/bin/binarywriter.cpp
Normal file
105
test/testfiles/bin/binarywriter.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <typeinfo>
|
||||
#include <cxxabi.h>
|
||||
|
||||
std::string DemangleTypeName (const char* name)
|
||||
{
|
||||
int status = -4;
|
||||
char* demangled = abi::__cxa_demangle (name, NULL, NULL, &status);
|
||||
std::string result;
|
||||
if (status == 0) {
|
||||
result = demangled;
|
||||
} else {
|
||||
result = name;
|
||||
}
|
||||
delete[] demangled;
|
||||
return result;
|
||||
}
|
||||
|
||||
class BinaryWriter
|
||||
{
|
||||
public:
|
||||
BinaryWriter (const std::string& logFileName, const std::string& binaryFileName)
|
||||
{
|
||||
logFile.open (logFileName.c_str (), std::ofstream::out);
|
||||
binaryFile.open (binaryFileName.c_str (), std::ofstream::binary);
|
||||
}
|
||||
|
||||
~BinaryWriter ()
|
||||
{
|
||||
logFile.close ();
|
||||
binaryFile.close ();
|
||||
}
|
||||
|
||||
template<class Type>
|
||||
void Write (Type value)
|
||||
{
|
||||
logFile << value << " (" << DemangleTypeName (typeid (value).name ()) << ", " << sizeof (value) << ")" << std::endl;
|
||||
binaryFile.write ((char*) &value, sizeof (value));
|
||||
}
|
||||
|
||||
private:
|
||||
std::ofstream logFile;
|
||||
std::ofstream binaryFile;
|
||||
};
|
||||
|
||||
int main ()
|
||||
{
|
||||
BinaryWriter bw ("result.txt", "result.bin");
|
||||
|
||||
bw.Write ((bool) true);
|
||||
bw.Write ((bool) false);
|
||||
|
||||
bw.Write ((char) 'a');
|
||||
bw.Write ((char) 'A');
|
||||
bw.Write ((unsigned char) 'a');
|
||||
bw.Write ((unsigned char) 'A');
|
||||
|
||||
bw.Write ((short) 42);
|
||||
bw.Write ((short) -42);
|
||||
bw.Write ((short) 32000);
|
||||
bw.Write ((short) -32000);
|
||||
bw.Write ((unsigned short) 42);
|
||||
bw.Write ((unsigned short) -42);
|
||||
bw.Write ((unsigned short) 32000);
|
||||
bw.Write ((unsigned short) -32000);
|
||||
|
||||
bw.Write ((int) 42);
|
||||
bw.Write ((int) -42);
|
||||
bw.Write ((int) 32000);
|
||||
bw.Write ((int) -32000);
|
||||
bw.Write ((int) 2000000000);
|
||||
bw.Write ((int) -2000000000);
|
||||
bw.Write ((unsigned int) 42);
|
||||
bw.Write ((unsigned int) -42);
|
||||
bw.Write ((unsigned int) 32000);
|
||||
bw.Write ((unsigned int) -32000);
|
||||
bw.Write ((unsigned int) 2000000000);
|
||||
bw.Write ((unsigned int) -2000000000);
|
||||
|
||||
bw.Write ((long) 42);
|
||||
bw.Write ((long) -42);
|
||||
bw.Write ((long) 32000);
|
||||
bw.Write ((long) -32000);
|
||||
bw.Write ((long) 2000000000);
|
||||
bw.Write ((long) -2000000000);
|
||||
bw.Write ((unsigned long) 42);
|
||||
bw.Write ((unsigned long) -42);
|
||||
bw.Write ((unsigned long) 32000);
|
||||
bw.Write ((unsigned long) -32000);
|
||||
bw.Write ((unsigned long) 2000000000);
|
||||
bw.Write ((unsigned long) -2000000000);
|
||||
|
||||
bw.Write ((float) 42);
|
||||
bw.Write ((float) -42);
|
||||
bw.Write ((float) 12345.6789);
|
||||
bw.Write ((float) -12345.6789);
|
||||
|
||||
bw.Write ((double) 42);
|
||||
bw.Write ((double) -42);
|
||||
bw.Write ((double) 12345.6789);
|
||||
bw.Write ((double) -12345.6789);
|
||||
|
||||
return 0;
|
||||
}
|
||||
10
test/testfiles/gltf/Box/README.md
Normal file
10
test/testfiles/gltf/Box/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
# Box
|
||||
## Screenshot
|
||||
|
||||

|
||||
|
||||
## License Information
|
||||
|
||||
Donated by [Cesium](http://cesiumjs.org/) for glTF testing.
|
||||
|
||||
This model is licensed under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by/4.0/).
|
||||
BIN
test/testfiles/gltf/Box/glTF-Binary/Box.glb
Normal file
BIN
test/testfiles/gltf/Box/glTF-Binary/Box.glb
Normal file
Binary file not shown.
BIN
test/testfiles/gltf/Box/glTF-Draco/Box.bin
Normal file
BIN
test/testfiles/gltf/Box/glTF-Draco/Box.bin
Normal file
Binary file not shown.
152
test/testfiles/gltf/Box/glTF-Draco/Box.gltf
Normal file
152
test/testfiles/gltf/Box/glTF-Draco/Box.gltf
Normal file
@ -0,0 +1,152 @@
|
||||
{
|
||||
"asset": {
|
||||
"generator": "COLLADA2GLTF",
|
||||
"version": "2.0"
|
||||
},
|
||||
"scene": 0,
|
||||
"scenes": [
|
||||
{
|
||||
"nodes": [
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"children": [
|
||||
1
|
||||
],
|
||||
"matrix": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
-1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"mesh": 0
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"NORMAL": 1,
|
||||
"POSITION": 2
|
||||
},
|
||||
"indices": 0,
|
||||
"mode": 4,
|
||||
"material": 0,
|
||||
"extensions": {
|
||||
"KHR_draco_mesh_compression": {
|
||||
"bufferView": 0,
|
||||
"attributes": {
|
||||
"NORMAL": 0,
|
||||
"POSITION": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"name": "Mesh"
|
||||
}
|
||||
],
|
||||
"accessors": [
|
||||
{
|
||||
"componentType": 5123,
|
||||
"count": 36,
|
||||
"max": [
|
||||
23
|
||||
],
|
||||
"min": [
|
||||
0
|
||||
],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [
|
||||
1.007843137254902,
|
||||
1.007843137254902,
|
||||
1.007843137254902
|
||||
],
|
||||
"min": [
|
||||
-1.007843137254902,
|
||||
-1.007843137254902,
|
||||
-1.007843137254902
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [
|
||||
0.5004885197850513,
|
||||
0.5004885197850513,
|
||||
0.5004885197850513
|
||||
],
|
||||
"min": [
|
||||
-0.5004885197850513,
|
||||
-0.5004885197850513,
|
||||
-0.5004885197850513
|
||||
],
|
||||
"type": "VEC3"
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"pbrMetallicRoughness": {
|
||||
"baseColorFactor": [
|
||||
0.800000011920929,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
],
|
||||
"metallicFactor": 0,
|
||||
"roughnessFactor": 1
|
||||
},
|
||||
"name": "Red",
|
||||
"emissiveFactor": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"alphaMode": "OPAQUE",
|
||||
"doubleSided": false
|
||||
}
|
||||
],
|
||||
"bufferViews": [
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 0,
|
||||
"byteLength": 118
|
||||
}
|
||||
],
|
||||
"buffers": [
|
||||
{
|
||||
"name": "Box",
|
||||
"byteLength": 120,
|
||||
"uri": "Box.bin"
|
||||
}
|
||||
],
|
||||
"extensionsRequired": [
|
||||
"KHR_draco_mesh_compression"
|
||||
],
|
||||
"extensionsUsed": [
|
||||
"KHR_draco_mesh_compression"
|
||||
]
|
||||
}
|
||||
142
test/testfiles/gltf/Box/glTF-Embedded/Box.gltf
Normal file
142
test/testfiles/gltf/Box/glTF-Embedded/Box.gltf
Normal file
@ -0,0 +1,142 @@
|
||||
{
|
||||
"asset": {
|
||||
"generator": "COLLADA2GLTF",
|
||||
"version": "2.0"
|
||||
},
|
||||
"scene": 0,
|
||||
"scenes": [
|
||||
{
|
||||
"nodes": [
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"children": [
|
||||
1
|
||||
],
|
||||
"matrix": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
-1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"mesh": 0
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"NORMAL": 1,
|
||||
"POSITION": 2
|
||||
},
|
||||
"indices": 0,
|
||||
"mode": 4,
|
||||
"material": 0
|
||||
}
|
||||
],
|
||||
"name": "Mesh"
|
||||
}
|
||||
],
|
||||
"accessors": [
|
||||
{
|
||||
"bufferView": 0,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5123,
|
||||
"count": 36,
|
||||
"max": [
|
||||
23
|
||||
],
|
||||
"min": [
|
||||
0
|
||||
],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView": 1,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"min": [
|
||||
-1.0,
|
||||
-1.0,
|
||||
-1.0
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 1,
|
||||
"byteOffset": 288,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.5
|
||||
],
|
||||
"min": [
|
||||
-0.5,
|
||||
-0.5,
|
||||
-0.5
|
||||
],
|
||||
"type": "VEC3"
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"pbrMetallicRoughness": {
|
||||
"baseColorFactor": [
|
||||
0.800000011920929,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"metallicFactor": 0.0
|
||||
},
|
||||
"name": "Red"
|
||||
}
|
||||
],
|
||||
"bufferViews": [
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 576,
|
||||
"byteLength": 72,
|
||||
"target": 34963
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 0,
|
||||
"byteLength": 576,
|
||||
"byteStride": 12,
|
||||
"target": 34962
|
||||
}
|
||||
],
|
||||
"buffers": [
|
||||
{
|
||||
"byteLength": 648,
|
||||
"uri": "data:application/octet-stream;base64,AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAvwAAAL8AAAA/AAAAPwAAAL8AAAA/AAAAvwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAL8AAAA/AAAAvwAAAL8AAAA/AAAAPwAAAL8AAAC/AAAAvwAAAL8AAAC/AAAAPwAAAD8AAAA/AAAAPwAAAL8AAAA/AAAAPwAAAD8AAAC/AAAAPwAAAL8AAAC/AAAAvwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAvwAAAD8AAAC/AAAAPwAAAD8AAAC/AAAAvwAAAL8AAAA/AAAAvwAAAD8AAAA/AAAAvwAAAL8AAAC/AAAAvwAAAD8AAAC/AAAAvwAAAL8AAAC/AAAAvwAAAD8AAAC/AAAAPwAAAL8AAAC/AAAAPwAAAD8AAAC/AAABAAIAAwACAAEABAAFAAYABwAGAAUACAAJAAoACwAKAAkADAANAA4ADwAOAA0AEAARABIAEwASABEAFAAVABYAFwAWABUA"
|
||||
}
|
||||
]
|
||||
}
|
||||
142
test/testfiles/gltf/Box/glTF/Box.gltf
Normal file
142
test/testfiles/gltf/Box/glTF/Box.gltf
Normal file
@ -0,0 +1,142 @@
|
||||
{
|
||||
"asset": {
|
||||
"generator": "COLLADA2GLTF",
|
||||
"version": "2.0"
|
||||
},
|
||||
"scene": 0,
|
||||
"scenes": [
|
||||
{
|
||||
"nodes": [
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"children": [
|
||||
1
|
||||
],
|
||||
"matrix": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
-1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"mesh": 0
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"NORMAL": 1,
|
||||
"POSITION": 2
|
||||
},
|
||||
"indices": 0,
|
||||
"mode": 4,
|
||||
"material": 0
|
||||
}
|
||||
],
|
||||
"name": "Mesh"
|
||||
}
|
||||
],
|
||||
"accessors": [
|
||||
{
|
||||
"bufferView": 0,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5123,
|
||||
"count": 36,
|
||||
"max": [
|
||||
23
|
||||
],
|
||||
"min": [
|
||||
0
|
||||
],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView": 1,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"min": [
|
||||
-1.0,
|
||||
-1.0,
|
||||
-1.0
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 1,
|
||||
"byteOffset": 288,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.5
|
||||
],
|
||||
"min": [
|
||||
-0.5,
|
||||
-0.5,
|
||||
-0.5
|
||||
],
|
||||
"type": "VEC3"
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"pbrMetallicRoughness": {
|
||||
"baseColorFactor": [
|
||||
0.800000011920929,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"metallicFactor": 0.0
|
||||
},
|
||||
"name": "Red"
|
||||
}
|
||||
],
|
||||
"bufferViews": [
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 576,
|
||||
"byteLength": 72,
|
||||
"target": 34963
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 0,
|
||||
"byteLength": 576,
|
||||
"byteStride": 12,
|
||||
"target": 34962
|
||||
}
|
||||
],
|
||||
"buffers": [
|
||||
{
|
||||
"byteLength": 648,
|
||||
"uri": "Box0.bin"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
test/testfiles/gltf/Box/glTF/Box0.bin
Normal file
BIN
test/testfiles/gltf/Box/glTF/Box0.bin
Normal file
Binary file not shown.
BIN
test/testfiles/gltf/Box/screenshot/screenshot.png
Normal file
BIN
test/testfiles/gltf/Box/screenshot/screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
10
test/testfiles/gltf/BoxInterleaved/README.md
Normal file
10
test/testfiles/gltf/BoxInterleaved/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
# Box with interleaved position and normal attributes
|
||||
## Screenshot
|
||||
|
||||

|
||||
|
||||
## License Information
|
||||
|
||||
Donated by [Cesium](http://cesiumjs.org/) and interleaved by [AngeloReppucci](https://github.com/AngeloReppucci) for glTF testing.
|
||||
|
||||
This model is licensed under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by/4.0/).
|
||||
Binary file not shown.
@ -0,0 +1,140 @@
|
||||
{
|
||||
"asset": {
|
||||
"generator": "COLLADA2GLTF",
|
||||
"version": "2.0"
|
||||
},
|
||||
"scene": 0,
|
||||
"scenes": [
|
||||
{
|
||||
"nodes": [
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"children": [
|
||||
1
|
||||
],
|
||||
"matrix": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
-1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"mesh": 0
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"NORMAL": 1,
|
||||
"POSITION": 2
|
||||
},
|
||||
"indices": 0,
|
||||
"mode": 4,
|
||||
"material": 0
|
||||
}
|
||||
],
|
||||
"name": "Mesh"
|
||||
}
|
||||
],
|
||||
"accessors": [
|
||||
{
|
||||
"bufferView": 0,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5123,
|
||||
"count": 36,
|
||||
"max": [
|
||||
23
|
||||
],
|
||||
"min": [
|
||||
0
|
||||
],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView": 1,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"min": [
|
||||
-1.0,
|
||||
-1.0,
|
||||
-1.0
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 1,
|
||||
"byteOffset": 12,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.5
|
||||
],
|
||||
"min": [
|
||||
-0.5,
|
||||
-0.5,
|
||||
-0.5
|
||||
],
|
||||
"type": "VEC3"
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"pbrMetallicRoughness": {
|
||||
"baseColorFactor": [
|
||||
0.800000011920929,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"bufferViews": [
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 576,
|
||||
"byteLength": 72,
|
||||
"target": 34963
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 0,
|
||||
"byteLength": 576,
|
||||
"byteStride": 24,
|
||||
"target": 34962
|
||||
}
|
||||
],
|
||||
"buffers": [
|
||||
{
|
||||
"byteLength": 648,
|
||||
"uri": "data:application/octet-stream;base64,AAAAAAAAAAAAAIA/AAAAvwAAAL8AAAA/AAAAAAAAAAAAAIA/AAAAPwAAAL8AAAA/AAAAAAAAAAAAAIA/AAAAvwAAAD8AAAA/AAAAAAAAAAAAAIA/AAAAPwAAAD8AAAA/AAAAAAAAgL8AAAAAAAAAPwAAAL8AAAA/AAAAAAAAgL8AAAAAAAAAvwAAAL8AAAA/AAAAAAAAgL8AAAAAAAAAPwAAAL8AAAC/AAAAAAAAgL8AAAAAAAAAvwAAAL8AAAC/AACAPwAAAAAAAAAAAAAAPwAAAD8AAAA/AACAPwAAAAAAAAAAAAAAPwAAAL8AAAA/AACAPwAAAAAAAAAAAAAAPwAAAD8AAAC/AACAPwAAAAAAAAAAAAAAPwAAAL8AAAC/AAAAAAAAgD8AAAAAAAAAvwAAAD8AAAA/AAAAAAAAgD8AAAAAAAAAPwAAAD8AAAA/AAAAAAAAgD8AAAAAAAAAvwAAAD8AAAC/AAAAAAAAgD8AAAAAAAAAPwAAAD8AAAC/AACAvwAAAAAAAAAAAAAAvwAAAL8AAAA/AACAvwAAAAAAAAAAAAAAvwAAAD8AAAA/AACAvwAAAAAAAAAAAAAAvwAAAL8AAAC/AACAvwAAAAAAAAAAAAAAvwAAAD8AAAC/AAAAAAAAAAAAAIC/AAAAvwAAAL8AAAC/AAAAAAAAAAAAAIC/AAAAvwAAAD8AAAC/AAAAAAAAAAAAAIC/AAAAPwAAAL8AAAC/AAAAAAAAAAAAAIC/AAAAPwAAAD8AAAC/AAABAAIAAwACAAEABAAFAAYABwAGAAUACAAJAAoACwAKAAkADAANAA4ADwAOAA0AEAARABIAEwASABEAFAAVABYAFwAWABUA"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
test/testfiles/gltf/BoxInterleaved/glTF/BoxInterleaved.bin
Normal file
BIN
test/testfiles/gltf/BoxInterleaved/glTF/BoxInterleaved.bin
Normal file
Binary file not shown.
140
test/testfiles/gltf/BoxInterleaved/glTF/BoxInterleaved.gltf
Normal file
140
test/testfiles/gltf/BoxInterleaved/glTF/BoxInterleaved.gltf
Normal file
@ -0,0 +1,140 @@
|
||||
{
|
||||
"asset": {
|
||||
"generator": "COLLADA2GLTF",
|
||||
"version": "2.0"
|
||||
},
|
||||
"scene": 0,
|
||||
"scenes": [
|
||||
{
|
||||
"nodes": [
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"children": [
|
||||
1
|
||||
],
|
||||
"matrix": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
-1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"mesh": 0
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"NORMAL": 1,
|
||||
"POSITION": 2
|
||||
},
|
||||
"indices": 0,
|
||||
"mode": 4,
|
||||
"material": 0
|
||||
}
|
||||
],
|
||||
"name": "Mesh"
|
||||
}
|
||||
],
|
||||
"accessors": [
|
||||
{
|
||||
"bufferView": 0,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5123,
|
||||
"count": 36,
|
||||
"max": [
|
||||
23
|
||||
],
|
||||
"min": [
|
||||
0
|
||||
],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView": 1,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"min": [
|
||||
-1.0,
|
||||
-1.0,
|
||||
-1.0
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 1,
|
||||
"byteOffset": 12,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.5
|
||||
],
|
||||
"min": [
|
||||
-0.5,
|
||||
-0.5,
|
||||
-0.5
|
||||
],
|
||||
"type": "VEC3"
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"pbrMetallicRoughness": {
|
||||
"baseColorFactor": [
|
||||
0.800000011920929,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"bufferViews": [
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 576,
|
||||
"byteLength": 72,
|
||||
"target": 34963
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 0,
|
||||
"byteLength": 576,
|
||||
"byteStride": 24,
|
||||
"target": 34962
|
||||
}
|
||||
],
|
||||
"buffers": [
|
||||
{
|
||||
"byteLength": 648,
|
||||
"uri": "BoxInterleaved.bin"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
test/testfiles/gltf/BoxInterleaved/screenshot/screenshot.png
Normal file
BIN
test/testfiles/gltf/BoxInterleaved/screenshot/screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
10
test/testfiles/gltf/BoxTextured/README.md
Normal file
10
test/testfiles/gltf/BoxTextured/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
# Box Textured
|
||||
## Screenshot
|
||||
|
||||

|
||||
|
||||
## License Information
|
||||
|
||||
Donated by Cesium for glTF testing. Please follow the [Cesium Trademark Terms and Conditions](https://github.com/AnalyticalGraphicsInc/cesium/wiki/CesiumTrademark.pdf).
|
||||
|
||||
This model is licensed under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by/4.0/).
|
||||
BIN
test/testfiles/gltf/BoxTextured/glTF-Binary/BoxTextured.glb
Normal file
BIN
test/testfiles/gltf/BoxTextured/glTF-Binary/BoxTextured.glb
Normal file
Binary file not shown.
181
test/testfiles/gltf/BoxTextured/glTF-Embedded/BoxTextured.gltf
Normal file
181
test/testfiles/gltf/BoxTextured/glTF-Embedded/BoxTextured.gltf
Normal file
File diff suppressed because one or more lines are too long
181
test/testfiles/gltf/BoxTextured/glTF/BoxTextured.gltf
Normal file
181
test/testfiles/gltf/BoxTextured/glTF/BoxTextured.gltf
Normal file
@ -0,0 +1,181 @@
|
||||
{
|
||||
"asset": {
|
||||
"generator": "COLLADA2GLTF",
|
||||
"version": "2.0"
|
||||
},
|
||||
"scene": 0,
|
||||
"scenes": [
|
||||
{
|
||||
"nodes": [
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"children": [
|
||||
1
|
||||
],
|
||||
"matrix": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
-1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"mesh": 0
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"NORMAL": 1,
|
||||
"POSITION": 2,
|
||||
"TEXCOORD_0": 3
|
||||
},
|
||||
"indices": 0,
|
||||
"mode": 4,
|
||||
"material": 0
|
||||
}
|
||||
],
|
||||
"name": "Mesh"
|
||||
}
|
||||
],
|
||||
"accessors": [
|
||||
{
|
||||
"bufferView": 0,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5123,
|
||||
"count": 36,
|
||||
"max": [
|
||||
23
|
||||
],
|
||||
"min": [
|
||||
0
|
||||
],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView": 1,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"min": [
|
||||
-1.0,
|
||||
-1.0,
|
||||
-1.0
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 1,
|
||||
"byteOffset": 288,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.5
|
||||
],
|
||||
"min": [
|
||||
-0.5,
|
||||
-0.5,
|
||||
-0.5
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 2,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [
|
||||
6.0,
|
||||
1.0
|
||||
],
|
||||
"min": [
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"type": "VEC2"
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"pbrMetallicRoughness": {
|
||||
"baseColorTexture": {
|
||||
"index": 0
|
||||
},
|
||||
"metallicFactor": 0.0
|
||||
},
|
||||
"name": "Texture"
|
||||
}
|
||||
],
|
||||
"textures": [
|
||||
{
|
||||
"sampler": 0,
|
||||
"source": 0
|
||||
}
|
||||
],
|
||||
"images": [
|
||||
{
|
||||
"uri": "CesiumLogoFlat.png"
|
||||
}
|
||||
],
|
||||
"samplers": [
|
||||
{
|
||||
"magFilter": 9729,
|
||||
"minFilter": 9986,
|
||||
"wrapS": 10497,
|
||||
"wrapT": 10497
|
||||
}
|
||||
],
|
||||
"bufferViews": [
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 768,
|
||||
"byteLength": 72,
|
||||
"target": 34963
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 0,
|
||||
"byteLength": 576,
|
||||
"byteStride": 12,
|
||||
"target": 34962
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 576,
|
||||
"byteLength": 192,
|
||||
"byteStride": 8,
|
||||
"target": 34962
|
||||
}
|
||||
],
|
||||
"buffers": [
|
||||
{
|
||||
"byteLength": 840,
|
||||
"uri": "BoxTextured0.bin"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
test/testfiles/gltf/BoxTextured/glTF/BoxTextured0.bin
Normal file
BIN
test/testfiles/gltf/BoxTextured/glTF/BoxTextured0.bin
Normal file
Binary file not shown.
BIN
test/testfiles/gltf/BoxTextured/glTF/CesiumLogoFlat.png
Normal file
BIN
test/testfiles/gltf/BoxTextured/glTF/CesiumLogoFlat.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
BIN
test/testfiles/gltf/BoxTextured/screenshot/screenshot.png
Normal file
BIN
test/testfiles/gltf/BoxTextured/screenshot/screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
21
test/testfiles/gltf/Mesh_PrimitiveMode/LICENSE
Normal file
21
test/testfiles/gltf/Mesh_PrimitiveMode/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Khronos Group
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
BIN
test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_00.bin
Normal file
BIN
test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_00.bin
Normal file
Binary file not shown.
@ -0,0 +1,71 @@
|
||||
{
|
||||
"accessors": [
|
||||
{
|
||||
"bufferView": 0,
|
||||
"componentType": 5126,
|
||||
"count": 1024,
|
||||
"type": "VEC3",
|
||||
"max": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.0
|
||||
],
|
||||
"min": [
|
||||
-0.5,
|
||||
-0.5,
|
||||
0.0
|
||||
],
|
||||
"name": "Positions Accessor"
|
||||
}
|
||||
],
|
||||
"asset": {
|
||||
"generator": "glTF Asset Generator",
|
||||
"version": "2.0"
|
||||
},
|
||||
"buffers": [
|
||||
{
|
||||
"uri": "Mesh_PrimitiveMode_00.bin",
|
||||
"byteLength": 12288
|
||||
}
|
||||
],
|
||||
"bufferViews": [
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 12288,
|
||||
"name": "Positions"
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"pbrMetallicRoughness": {
|
||||
"metallicFactor": 0.0
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"POSITION": 0
|
||||
},
|
||||
"material": 0,
|
||||
"mode": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"mesh": 0
|
||||
}
|
||||
],
|
||||
"scene": 0,
|
||||
"scenes": [
|
||||
{
|
||||
"nodes": [
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_01.bin
Normal file
BIN
test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_01.bin
Normal file
Binary file not shown.
@ -0,0 +1,71 @@
|
||||
{
|
||||
"accessors": [
|
||||
{
|
||||
"bufferView": 0,
|
||||
"componentType": 5126,
|
||||
"count": 8,
|
||||
"type": "VEC3",
|
||||
"max": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.0
|
||||
],
|
||||
"min": [
|
||||
-0.5,
|
||||
-0.5,
|
||||
0.0
|
||||
],
|
||||
"name": "Positions Accessor"
|
||||
}
|
||||
],
|
||||
"asset": {
|
||||
"generator": "glTF Asset Generator",
|
||||
"version": "2.0"
|
||||
},
|
||||
"buffers": [
|
||||
{
|
||||
"uri": "Mesh_PrimitiveMode_01.bin",
|
||||
"byteLength": 96
|
||||
}
|
||||
],
|
||||
"bufferViews": [
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 96,
|
||||
"name": "Positions"
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"pbrMetallicRoughness": {
|
||||
"metallicFactor": 0.0
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"POSITION": 0
|
||||
},
|
||||
"material": 0,
|
||||
"mode": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"mesh": 0
|
||||
}
|
||||
],
|
||||
"scene": 0,
|
||||
"scenes": [
|
||||
{
|
||||
"nodes": [
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_02.bin
Normal file
BIN
test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_02.bin
Normal file
Binary file not shown.
@ -0,0 +1,71 @@
|
||||
{
|
||||
"accessors": [
|
||||
{
|
||||
"bufferView": 0,
|
||||
"componentType": 5126,
|
||||
"count": 4,
|
||||
"type": "VEC3",
|
||||
"max": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.0
|
||||
],
|
||||
"min": [
|
||||
-0.5,
|
||||
-0.5,
|
||||
0.0
|
||||
],
|
||||
"name": "Positions Accessor"
|
||||
}
|
||||
],
|
||||
"asset": {
|
||||
"generator": "glTF Asset Generator",
|
||||
"version": "2.0"
|
||||
},
|
||||
"buffers": [
|
||||
{
|
||||
"uri": "Mesh_PrimitiveMode_02.bin",
|
||||
"byteLength": 48
|
||||
}
|
||||
],
|
||||
"bufferViews": [
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 48,
|
||||
"name": "Positions"
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"pbrMetallicRoughness": {
|
||||
"metallicFactor": 0.0
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"POSITION": 0
|
||||
},
|
||||
"material": 0,
|
||||
"mode": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"mesh": 0
|
||||
}
|
||||
],
|
||||
"scene": 0,
|
||||
"scenes": [
|
||||
{
|
||||
"nodes": [
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_03.bin
Normal file
BIN
test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_03.bin
Normal file
Binary file not shown.
@ -0,0 +1,71 @@
|
||||
{
|
||||
"accessors": [
|
||||
{
|
||||
"bufferView": 0,
|
||||
"componentType": 5126,
|
||||
"count": 5,
|
||||
"type": "VEC3",
|
||||
"max": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.0
|
||||
],
|
||||
"min": [
|
||||
-0.5,
|
||||
-0.5,
|
||||
0.0
|
||||
],
|
||||
"name": "Positions Accessor"
|
||||
}
|
||||
],
|
||||
"asset": {
|
||||
"generator": "glTF Asset Generator",
|
||||
"version": "2.0"
|
||||
},
|
||||
"buffers": [
|
||||
{
|
||||
"uri": "Mesh_PrimitiveMode_03.bin",
|
||||
"byteLength": 60
|
||||
}
|
||||
],
|
||||
"bufferViews": [
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 60,
|
||||
"name": "Positions"
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"pbrMetallicRoughness": {
|
||||
"metallicFactor": 0.0
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"POSITION": 0
|
||||
},
|
||||
"material": 0,
|
||||
"mode": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"mesh": 0
|
||||
}
|
||||
],
|
||||
"scene": 0,
|
||||
"scenes": [
|
||||
{
|
||||
"nodes": [
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_04.bin
Normal file
BIN
test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_04.bin
Normal file
Binary file not shown.
@ -0,0 +1,71 @@
|
||||
{
|
||||
"accessors": [
|
||||
{
|
||||
"bufferView": 0,
|
||||
"componentType": 5126,
|
||||
"count": 4,
|
||||
"type": "VEC3",
|
||||
"max": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.0
|
||||
],
|
||||
"min": [
|
||||
-0.5,
|
||||
-0.5,
|
||||
0.0
|
||||
],
|
||||
"name": "Positions Accessor"
|
||||
}
|
||||
],
|
||||
"asset": {
|
||||
"generator": "glTF Asset Generator",
|
||||
"version": "2.0"
|
||||
},
|
||||
"buffers": [
|
||||
{
|
||||
"uri": "Mesh_PrimitiveMode_04.bin",
|
||||
"byteLength": 48
|
||||
}
|
||||
],
|
||||
"bufferViews": [
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 48,
|
||||
"name": "Positions"
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"pbrMetallicRoughness": {
|
||||
"metallicFactor": 0.0
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"POSITION": 0
|
||||
},
|
||||
"material": 0,
|
||||
"mode": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"mesh": 0
|
||||
}
|
||||
],
|
||||
"scene": 0,
|
||||
"scenes": [
|
||||
{
|
||||
"nodes": [
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_05.bin
Normal file
BIN
test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_05.bin
Normal file
Binary file not shown.
@ -0,0 +1,71 @@
|
||||
{
|
||||
"accessors": [
|
||||
{
|
||||
"bufferView": 0,
|
||||
"componentType": 5126,
|
||||
"count": 4,
|
||||
"type": "VEC3",
|
||||
"max": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.0
|
||||
],
|
||||
"min": [
|
||||
-0.5,
|
||||
-0.5,
|
||||
0.0
|
||||
],
|
||||
"name": "Positions Accessor"
|
||||
}
|
||||
],
|
||||
"asset": {
|
||||
"generator": "glTF Asset Generator",
|
||||
"version": "2.0"
|
||||
},
|
||||
"buffers": [
|
||||
{
|
||||
"uri": "Mesh_PrimitiveMode_05.bin",
|
||||
"byteLength": 48
|
||||
}
|
||||
],
|
||||
"bufferViews": [
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 48,
|
||||
"name": "Positions"
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"pbrMetallicRoughness": {
|
||||
"metallicFactor": 0.0
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"POSITION": 0
|
||||
},
|
||||
"material": 0,
|
||||
"mode": 6
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"mesh": 0
|
||||
}
|
||||
],
|
||||
"scene": 0,
|
||||
"scenes": [
|
||||
{
|
||||
"nodes": [
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_06.bin
Normal file
BIN
test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_06.bin
Normal file
Binary file not shown.
@ -0,0 +1,70 @@
|
||||
{
|
||||
"accessors": [
|
||||
{
|
||||
"bufferView": 0,
|
||||
"componentType": 5126,
|
||||
"count": 6,
|
||||
"type": "VEC3",
|
||||
"max": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.0
|
||||
],
|
||||
"min": [
|
||||
-0.5,
|
||||
-0.5,
|
||||
0.0
|
||||
],
|
||||
"name": "Positions Accessor"
|
||||
}
|
||||
],
|
||||
"asset": {
|
||||
"generator": "glTF Asset Generator",
|
||||
"version": "2.0"
|
||||
},
|
||||
"buffers": [
|
||||
{
|
||||
"uri": "Mesh_PrimitiveMode_06.bin",
|
||||
"byteLength": 72
|
||||
}
|
||||
],
|
||||
"bufferViews": [
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 72,
|
||||
"name": "Positions"
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"pbrMetallicRoughness": {
|
||||
"metallicFactor": 0.0
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"POSITION": 0
|
||||
},
|
||||
"material": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"mesh": 0
|
||||
}
|
||||
],
|
||||
"scene": 0,
|
||||
"scenes": [
|
||||
{
|
||||
"nodes": [
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user