Merge branch 'dev'
This commit is contained in:
commit
3c6e3c8ae2
@ -21,6 +21,7 @@
|
||||
"globals" : {
|
||||
"OV" : true,
|
||||
"THREE" : false,
|
||||
"TextEncoder" : false,
|
||||
"TextDecoder" : false,
|
||||
"XMLHttpRequest" : false,
|
||||
"FileReader" : false,
|
||||
|
||||
20
README.md
20
README.md
@ -5,23 +5,29 @@ This repository contains the source code of the https://3dviewer.net website. Th
|
||||
[](https://ci.appveyor.com/project/kovacsv/online3dviewer)
|
||||
[](https://travis-ci.com/kovacsv/Online3DViewer)
|
||||
|
||||
## Documentation
|
||||
|
||||
- [User Documentation](https://3dviewer.net/info)
|
||||
- [Developer Documentation](https://github.com/kovacsv/Online3DViewer/wiki)
|
||||
|
||||
## 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)
|
||||
- stl (text and binary)
|
||||
- ply (text and binary)
|
||||
- gltf (text and binary)
|
||||
- off (text only)
|
||||
|
||||
### Export
|
||||
|
||||
- obj (with mtl)
|
||||
- stl (ascii and binary)
|
||||
- ply (ascii and binary)
|
||||
- off (ascii only)
|
||||
- stl (text and binary)
|
||||
- ply (text and binary)
|
||||
- gltf (text and binary)
|
||||
- off (text only)
|
||||
|
||||
## Features
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@ test_script:
|
||||
- npm run test
|
||||
|
||||
artifacts:
|
||||
- path: build\package\*
|
||||
- path: build\final\package\*
|
||||
|
||||
deploy:
|
||||
description: 'News:\n- ...'
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "online-3d-viewer",
|
||||
"description": "Online 3D Viewer",
|
||||
"version": "0.7.2",
|
||||
"version": "0.7.3",
|
||||
"repository": "github:kovacsv/Online3DViewer",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
|
||||
@ -11,13 +11,13 @@ OV.Exporter = class
|
||||
];
|
||||
}
|
||||
|
||||
Export (model, format, extension, callbacks)
|
||||
Export (model, format, extension)
|
||||
{
|
||||
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);
|
||||
exporter.Export (model, format, files);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ OV.ExporterBase = class
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
this.callbacks = null;
|
||||
|
||||
}
|
||||
|
||||
CanExport (format, extension)
|
||||
@ -50,9 +50,8 @@ OV.ExporterBase = class
|
||||
return false;
|
||||
}
|
||||
|
||||
Export (model, format, files, callbacks)
|
||||
Export (model, format, files)
|
||||
{
|
||||
this.callbacks = callbacks;
|
||||
this.ExportContent (model, format, files);
|
||||
}
|
||||
|
||||
|
||||
@ -72,18 +72,31 @@ OV.ExporterGltf = class extends OV.ExporterBase
|
||||
|
||||
ExportBinaryContent (model, files)
|
||||
{
|
||||
function AlignToBoundary (size)
|
||||
{
|
||||
let remainder = size % 4;
|
||||
if (remainder === 0) {
|
||||
return size;
|
||||
}
|
||||
return size + (4 - remainder);
|
||||
}
|
||||
|
||||
function WriteCharacters (writer, char, count)
|
||||
{
|
||||
for (let i = 0; i < count; i++) {
|
||||
writer.WriteUnsignedCharacter8 (char);
|
||||
}
|
||||
}
|
||||
|
||||
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 textureOffset = mainBuffer.byteLength;
|
||||
|
||||
let fileNameToIndex = [];
|
||||
this.ExportMaterials (model, mainJson, function (texture) {
|
||||
@ -91,20 +104,17 @@ OV.ExporterGltf = class extends OV.ExporterBase
|
||||
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);
|
||||
let textureBuffer = texture.buffer;
|
||||
textureBuffers.push (textureBuffer);
|
||||
mainJson.buffers.push ({
|
||||
byteLength : textureBuffer.byteLength
|
||||
});
|
||||
mainJson.bufferViews.push ({
|
||||
buffer : bufferIndex,
|
||||
byteOffset : 0,
|
||||
buffer : 0,
|
||||
byteOffset : textureOffset,
|
||||
byteLength : textureBuffer.byteLength
|
||||
});
|
||||
textureOffset += textureBuffer.byteLength;
|
||||
mainJson.images.push ({
|
||||
bufferView : bufferViewIndex,
|
||||
mimeType : 'image/' + extension
|
||||
@ -116,34 +126,42 @@ OV.ExporterGltf = class extends OV.ExporterBase
|
||||
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 mainBinaryBufferLength = mainBuffer.byteLength;
|
||||
for (let i = 0; i < textureBuffers.length; i++) {
|
||||
let textureBuffer = textureBuffers[i];
|
||||
mainBinaryBufferLength += textureBuffer.byteLength;
|
||||
}
|
||||
let mainBinaryBufferAlignedLength = AlignToBoundary (mainBinaryBufferLength);
|
||||
mainJson.buffers.push ({
|
||||
byteLength : mainBinaryBufferAlignedLength
|
||||
});
|
||||
|
||||
let mainJsonString = JSON.stringify (mainJson);
|
||||
let mainJsonBuffer = OV.Utf8StringToArrayBuffer (mainJsonString);
|
||||
let mainJsonBufferLength = mainJsonBuffer.byteLength;
|
||||
let mainJsonBufferAlignedLength = AlignToBoundary (mainJsonBufferLength);
|
||||
|
||||
let glbSize = 12 + 8 + mainJsonBufferAlignedLength + 8 + mainBinaryBufferAlignedLength;
|
||||
let glbWriter = new OV.BinaryWriter (glbSize, true);
|
||||
|
||||
glbWriter.WriteUnsignedInteger32 (0x46546C67);
|
||||
glbWriter.WriteUnsignedInteger32 (2);
|
||||
glbWriter.WriteUnsignedInteger32 (glbSize);
|
||||
|
||||
glbWriter.WriteUnsignedInteger32 (mainJsonString.length);
|
||||
glbWriter.WriteUnsignedInteger32 (mainJsonBufferAlignedLength);
|
||||
glbWriter.WriteUnsignedInteger32 (0x4E4F534A);
|
||||
for (let i = 0; i < mainJsonString.length; i++) {
|
||||
glbWriter.WriteUnsignedCharacter8 (mainJsonString.charCodeAt (i));
|
||||
}
|
||||
glbWriter.WriteArrayBuffer (mainJsonBuffer);
|
||||
WriteCharacters (glbWriter, 32, mainJsonBufferAlignedLength - mainJsonBufferLength);
|
||||
|
||||
glbWriter.WriteUnsignedInteger32 (mainBuffer.byteLength);
|
||||
glbWriter.WriteUnsignedInteger32 (mainBinaryBufferAlignedLength);
|
||||
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);
|
||||
}
|
||||
WriteCharacters (glbWriter, 0, mainBinaryBufferAlignedLength - mainBinaryBufferLength);
|
||||
|
||||
glbFile.SetContent (glbWriter.GetBuffer ());
|
||||
}
|
||||
@ -207,6 +225,16 @@ OV.ExporterGltf = class extends OV.ExporterBase
|
||||
|
||||
GetMainJson (meshDataArr)
|
||||
{
|
||||
function AddBufferView (mainJson, offset, length)
|
||||
{
|
||||
mainJson.bufferViews.push ({
|
||||
buffer : 0,
|
||||
byteOffset : offset,
|
||||
byteLength : length,
|
||||
});
|
||||
return offset + length;
|
||||
}
|
||||
|
||||
let mainJson = {
|
||||
asset : {
|
||||
version : '2.0'
|
||||
@ -239,10 +267,14 @@ OV.ExporterGltf = class extends OV.ExporterBase
|
||||
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 bufferViewIndex = mainJson.bufferViews.length;
|
||||
let bufferViewOffset = meshData.offsets[primitiveIndex];
|
||||
bufferViewOffset = AddBufferView (mainJson, bufferViewOffset, primitive.indices.length * this.components.index.size);
|
||||
bufferViewOffset = AddBufferView (mainJson, bufferViewOffset, primitive.vertices.length * this.components.number.size);
|
||||
bufferViewOffset = AddBufferView (mainJson, bufferViewOffset, primitive.normals.length * this.components.number.size);
|
||||
|
||||
let accessorIndex = mainJson.accessors.length;
|
||||
let jsonPrimitive = {
|
||||
attributes : {
|
||||
POSITION : accessorIndex + 1,
|
||||
@ -253,48 +285,39 @@ OV.ExporterGltf = class extends OV.ExporterBase
|
||||
material : primitive.material
|
||||
};
|
||||
|
||||
mainJson.bufferViews.push ({
|
||||
buffer : 0,
|
||||
byteOffset : meshData.offsets[primitiveIndex],
|
||||
byteLength : meshData.sizes[primitiveIndex]
|
||||
});
|
||||
|
||||
let bounds = primitive.GetBounds ();
|
||||
mainJson.accessors.push ({
|
||||
bufferView : bufferViewIndex,
|
||||
byteOffset : accessorOffset,
|
||||
byteOffset : 0,
|
||||
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,
|
||||
bufferView : bufferViewIndex + 1,
|
||||
byteOffset : 0,
|
||||
componentType : this.components.number.type,
|
||||
count : primitive.vertices.length / 3,
|
||||
min : bounds.min,
|
||||
max : bounds.max,
|
||||
type : 'VEC3'
|
||||
});
|
||||
accessorOffset += primitive.vertices.length * this.components.number.size;
|
||||
|
||||
mainJson.accessors.push ({
|
||||
bufferView : bufferViewIndex,
|
||||
byteOffset : accessorOffset,
|
||||
bufferView : bufferViewIndex + 2,
|
||||
byteOffset : 0,
|
||||
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) {
|
||||
bufferViewOffset = AddBufferView (mainJson, bufferViewOffset, primitive.uvs.length * this.components.number.size);
|
||||
mainJson.accessors.push ({
|
||||
bufferView : bufferViewIndex,
|
||||
byteOffset : accessorOffset,
|
||||
bufferView : bufferViewIndex + 3,
|
||||
byteOffset : 0,
|
||||
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;
|
||||
}
|
||||
|
||||
@ -331,7 +354,7 @@ OV.ExporterGltf = class extends OV.ExporterBase
|
||||
|
||||
function GetTextureParams (mainJson, texture, addTexture)
|
||||
{
|
||||
if (texture === null || texture.name === null || texture.url === null) {
|
||||
if (texture === null || !texture.IsValid ()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -343,11 +366,18 @@ OV.ExporterGltf = class extends OV.ExporterBase
|
||||
}
|
||||
|
||||
let textureIndex = addTexture (texture);
|
||||
let textureParams = {
|
||||
let textureParams = {
|
||||
index : textureIndex
|
||||
};
|
||||
|
||||
if (texture.HasTransformation ()) {
|
||||
let extensionName = 'KHR_texture_transform';
|
||||
if (mainJson.extensionsUsed === undefined) {
|
||||
mainJson.extensionsUsed = [];
|
||||
}
|
||||
if (mainJson.extensionsUsed.indexOf (extensionName) === -1) {
|
||||
mainJson.extensionsUsed.push (extensionName);
|
||||
}
|
||||
textureParams.extensions = {
|
||||
KHR_texture_transform : {
|
||||
offset : [texture.offset.x, -texture.offset.y],
|
||||
@ -368,12 +398,11 @@ OV.ExporterGltf = class extends OV.ExporterBase
|
||||
roughnessFactor : 1.0
|
||||
},
|
||||
emissiveFactor : ColorToRGB (material.emissive),
|
||||
alphaMode : 'OPAQUE',
|
||||
alphaCutoff : material.alphaTest
|
||||
alphaMode : 'OPAQUE'
|
||||
};
|
||||
|
||||
if (material.transparent) {
|
||||
// TODO: mask?
|
||||
// TODO: mask, alphaCutoff?
|
||||
jsonMaterial.alphaMode = 'BLEND';
|
||||
}
|
||||
|
||||
|
||||
@ -14,18 +14,19 @@ OV.ExporterObj = class extends OV.ExporterBase
|
||||
{
|
||||
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]);
|
||||
if (texture === null || !texture.IsValid ()) {
|
||||
return;
|
||||
}
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
2
source/external/three.converter.js
vendored
2
source/external/three.converter.js
vendored
@ -15,7 +15,7 @@ OV.ConvertModelToThreeMeshes = function (model, callbacks)
|
||||
|
||||
function LoadTexture (threeMaterial, texture, onLoad)
|
||||
{
|
||||
if (texture === null || texture.url === null) {
|
||||
if (texture === null || !texture.IsValid ()) {
|
||||
return;
|
||||
}
|
||||
let loader = new THREE.TextureLoader ();
|
||||
|
||||
4
source/external/three.model.loader.js
vendored
4
source/external/three.model.loader.js
vendored
@ -53,8 +53,8 @@ OV.ThreeModelLoader = class
|
||||
obj.OnModelImported (importResult);
|
||||
ready ();
|
||||
},
|
||||
error : function (importerError) {
|
||||
obj.callbacks.onLoadError (importerError);
|
||||
error : function (importError) {
|
||||
obj.callbacks.onLoadError (importError);
|
||||
obj.inProgress = false;
|
||||
ready ();
|
||||
}
|
||||
|
||||
@ -179,14 +179,29 @@ OV.FileList = class
|
||||
}
|
||||
};
|
||||
|
||||
OV.ImporterErrorCode =
|
||||
OV.RevokeModelUrls = function (model)
|
||||
{
|
||||
if (model === null) {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < model.MaterialCount (); i++) {
|
||||
let material = model.GetMaterial (i);
|
||||
material.EnumerateTextureMaps (function (texture) {
|
||||
if (texture.url !== null) {
|
||||
OV.RevokeObjectUrl (texture.url);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
OV.ImportErrorCode =
|
||||
{
|
||||
NoImportableFile : 1,
|
||||
ImportFailed : 2,
|
||||
UnknownError : 3
|
||||
};
|
||||
|
||||
OV.ImporterError = class
|
||||
OV.ImportError = class
|
||||
{
|
||||
constructor (code, message)
|
||||
{
|
||||
@ -236,8 +251,8 @@ OV.Importer = class
|
||||
Import (callbacks)
|
||||
{
|
||||
let mainFile = this.fileList.GetMainFile ();
|
||||
if (mainFile === null || mainFile.file.content === null) {
|
||||
callbacks.error (new OV.ImporterError (OV.ImporterErrorCode.NoImportableFile, null));
|
||||
if (mainFile === null || mainFile.file === null || mainFile.file.content === null) {
|
||||
callbacks.error (new OV.ImportError (OV.ImportErrorCode.NoImportableFile, null));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -245,9 +260,6 @@ OV.Importer = class
|
||||
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, {
|
||||
@ -256,47 +268,24 @@ OV.Importer = class
|
||||
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;
|
||||
getFileBuffer : function (fileName) {
|
||||
let fileBuffer = null;
|
||||
let file = obj.fileList.FindFileByPath (fileName);
|
||||
if (file === null || file.content === null) {
|
||||
result.missingFiles.push (fileName);
|
||||
obj.missingFiles.push (fileName);
|
||||
fileBuffer = null;
|
||||
} else {
|
||||
result.usedFiles.push (fileName);
|
||||
fileBuffer = file.content;
|
||||
}
|
||||
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;
|
||||
return fileBuffer;
|
||||
}
|
||||
});
|
||||
|
||||
if (importer.IsError ()) {
|
||||
let message = importer.GetMessage ();
|
||||
callbacks.error (new OV.ImporterError (OV.ImporterErrorCode.ImportFailed, message));
|
||||
callbacks.error (new OV.ImportError (OV.ImportErrorCode.ImportFailed, message));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -350,5 +339,5 @@ OV.Importer = class
|
||||
IsOnlyFileSource (source)
|
||||
{
|
||||
return this.fileList.IsOnlySource (source);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -177,9 +177,12 @@ OV.Importer3ds = class extends OV.ImporterBase
|
||||
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);
|
||||
let textureBuffer = obj.GetTextureBuffer (textureName);
|
||||
texture.name = textureName;
|
||||
texture.url = textureObjectUrl;
|
||||
if (textureBuffer !== null) {
|
||||
texture.url = textureBuffer.url;
|
||||
texture.buffer = textureBuffer.buffer;
|
||||
}
|
||||
} else if (chunkId === OV.CHUNK3DS.MAT_TEXMAP_UOFFSET) {
|
||||
texture.offset.x = reader.ReadFloat32 ();
|
||||
} else if (chunkId === OV.CHUNK3DS.MAT_TEXMAP_VOFFSET) {
|
||||
@ -336,7 +339,7 @@ OV.Importer3ds = class extends OV.ImporterBase
|
||||
let v0 = reader.ReadUnsignedInteger16 ();
|
||||
let v1 = reader.ReadUnsignedInteger16 ();
|
||||
let v2 = reader.ReadUnsignedInteger16 ();
|
||||
let flags = reader.ReadUnsignedInteger16 ();
|
||||
reader.ReadUnsignedInteger16 (); // flags
|
||||
mesh.AddTriangle (new OV.Triangle (v0, v1, v2));
|
||||
}
|
||||
|
||||
|
||||
@ -1,18 +1,61 @@
|
||||
OV.ImporterBuffers = class
|
||||
{
|
||||
constructor (getBufferCallback)
|
||||
{
|
||||
this.getBufferCallback = getBufferCallback;
|
||||
this.fileBuffers = {};
|
||||
this.textureBuffers = {};
|
||||
}
|
||||
|
||||
GetFileBuffer (filePath)
|
||||
{
|
||||
let fileName = OV.GetFileName (filePath);
|
||||
let buffer = this.fileBuffers[fileName];
|
||||
if (buffer === undefined) {
|
||||
buffer = this.getBufferCallback (fileName);
|
||||
this.fileBuffers[fileName] = buffer;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
GetTextureBuffer (filePath)
|
||||
{
|
||||
let fileName = OV.GetFileName (filePath);
|
||||
let buffer = this.textureBuffers[fileName];
|
||||
if (buffer === undefined) {
|
||||
let texBuffer = this.getBufferCallback (fileName);
|
||||
if (texBuffer !== null) {
|
||||
buffer = {
|
||||
url : OV.CreateObjectUrl (texBuffer),
|
||||
buffer : texBuffer
|
||||
};
|
||||
} else {
|
||||
buffer = null;
|
||||
}
|
||||
this.textureBuffers[fileName] = buffer;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
};
|
||||
|
||||
OV.ImporterBase = class
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
this.extension = null;
|
||||
this.callbacks = null;
|
||||
this.buffers = null;
|
||||
this.model = null;
|
||||
this.error = null;
|
||||
this.message = null;
|
||||
this.buffers = null;
|
||||
}
|
||||
|
||||
Import (content, extension, callbacks)
|
||||
{
|
||||
this.extension = extension;
|
||||
this.callbacks = callbacks;
|
||||
this.buffers = new OV.ImporterBuffers (this.callbacks.getFileBuffer);
|
||||
this.model = new OV.Model ();
|
||||
this.error = false;
|
||||
this.message = null;
|
||||
@ -31,6 +74,16 @@ OV.ImporterBase = class
|
||||
OV.FinalizeModel (this.model, this.callbacks.getDefaultMaterial);
|
||||
}
|
||||
|
||||
GetFileBuffer (filePath)
|
||||
{
|
||||
return this.buffers.GetFileBuffer (filePath);
|
||||
}
|
||||
|
||||
GetTextureBuffer (filePath)
|
||||
{
|
||||
return this.buffers.GetTextureBuffer (filePath);
|
||||
}
|
||||
|
||||
ResetState ()
|
||||
{
|
||||
|
||||
|
||||
@ -351,12 +351,14 @@ OV.ImporterGltf = class extends OV.ImporterBase
|
||||
|
||||
this.bufferContents = null;
|
||||
this.gltfExtensions = null;
|
||||
this.imageIndexToTextureParams = null;
|
||||
}
|
||||
|
||||
ResetState ()
|
||||
{
|
||||
this.bufferContents = [];
|
||||
this.gltfExtensions = new OV.GltfExtensions ();
|
||||
this.imageIndexToTextureParams = {};
|
||||
}
|
||||
|
||||
CanImportExtension (extension)
|
||||
@ -400,10 +402,13 @@ OV.ImporterGltf = class extends OV.ImporterBase
|
||||
let buffer = null;
|
||||
let gltfBuffer = gltf.buffers[i];
|
||||
let base64Buffer = OV.Base64DataURIToArrayBuffer (gltfBuffer.uri);
|
||||
if (base64Buffer === null) {
|
||||
buffer = this.callbacks.getFileContent (gltfBuffer.uri);
|
||||
} else {
|
||||
if (base64Buffer !== null) {
|
||||
buffer = base64Buffer.buffer;
|
||||
} else {
|
||||
let fileBuffer = this.GetFileBuffer (gltfBuffer.uri);
|
||||
if (fileBuffer !== null) {
|
||||
buffer = fileBuffer;
|
||||
}
|
||||
}
|
||||
if (buffer === null) {
|
||||
this.SetError ();
|
||||
@ -613,31 +618,48 @@ OV.ImporterGltf = class extends OV.ImporterBase
|
||||
|
||||
let texture = new OV.TextureMap ();
|
||||
let gltfTexture = gltf.textures[gltfTextureRef.index];
|
||||
let gltfImage = gltf.images[gltfTexture.source];
|
||||
let gltfImageIndex = gltfTexture.source;
|
||||
let gltfImage = gltf.images[gltfImageIndex];
|
||||
|
||||
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);
|
||||
let textureParams = this.imageIndexToTextureParams[gltfImageIndex];
|
||||
if (textureParams === undefined) {
|
||||
textureParams = {
|
||||
name : null,
|
||||
url : null,
|
||||
buffer : null
|
||||
};
|
||||
let textureIndexString = gltfImageIndex.toString ();
|
||||
if (gltfImage.uri !== undefined) {
|
||||
let base64Buffer = OV.Base64DataURIToArrayBuffer (gltfImage.uri);
|
||||
if (base64Buffer !== null) {
|
||||
textureParams.name = 'Embedded_' + textureIndexString + GetTextureFileExtension (base64Buffer.mimeType);
|
||||
textureParams.url = OV.CreateObjectUrlWithMimeType (base64Buffer.buffer, base64Buffer.mimeType);
|
||||
textureParams.buffer = base64Buffer.buffer;
|
||||
} else {
|
||||
let textureBuffer = this.GetTextureBuffer (gltfImage.uri);
|
||||
textureParams.name = gltfImage.uri;
|
||||
if (textureBuffer !== null) {
|
||||
textureParams.url = textureBuffer.url;
|
||||
textureParams.buffer = textureBuffer.buffer;
|
||||
}
|
||||
}
|
||||
} else if (gltfImage.bufferView !== undefined) {
|
||||
let bufferView = gltf.bufferViews[gltfImage.bufferView];
|
||||
let reader = this.GetReaderFromBufferView (bufferView);
|
||||
if (reader !== null) {
|
||||
let buffer = reader.ReadArrayBuffer (bufferView.byteLength);
|
||||
textureParams.name = 'Binary_' + textureIndexString + GetTextureFileExtension (gltfImage.mimeType);
|
||||
textureParams.url = OV.CreateObjectUrlWithMimeType (buffer, gltfImage.mimeType);
|
||||
textureParams.buffer = buffer;
|
||||
}
|
||||
}
|
||||
this.imageIndexToTextureParams[gltfImageIndex] = textureParams;
|
||||
}
|
||||
|
||||
texture.name = textureParams.name;
|
||||
texture.url = textureParams.url;
|
||||
texture.buffer = textureParams.buffer;
|
||||
|
||||
this.gltfExtensions.ProcessTexture (gltfTextureRef, texture);
|
||||
return texture;
|
||||
}
|
||||
@ -741,9 +763,6 @@ OV.ImporterGltf = class extends OV.ImporterBase
|
||||
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) {
|
||||
@ -766,6 +785,8 @@ OV.ImporterGltf = class extends OV.ImporterBase
|
||||
reader.EnumerateData (function (data) {
|
||||
mesh.AddVertex (data);
|
||||
});
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasNormals) {
|
||||
|
||||
@ -166,13 +166,15 @@ OV.ImporterObj = class extends OV.ImporterBase
|
||||
);
|
||||
}
|
||||
|
||||
function CreateTexture (keyword, line, callbacks)
|
||||
function CreateTexture (importer, keyword, line)
|
||||
{
|
||||
let texture = new OV.TextureMap ();
|
||||
texture.name = OV.NameFromLine (line, keyword.length, '#');
|
||||
let textureObjectUrl = callbacks.getTextureObjectUrl (texture.name);
|
||||
if (textureObjectUrl !== null) {
|
||||
texture.url = textureObjectUrl;
|
||||
let textureName = OV.NameFromLine (line, keyword.length, '#');
|
||||
let textureBuffer = importer.GetTextureBuffer (textureName);
|
||||
texture.name = textureName;
|
||||
if (textureBuffer !== null) {
|
||||
texture.url = textureBuffer.url;
|
||||
texture.buffer = textureBuffer.buffer;
|
||||
}
|
||||
return texture;
|
||||
}
|
||||
@ -206,9 +208,9 @@ OV.ImporterObj = class extends OV.ImporterBase
|
||||
return true;
|
||||
}
|
||||
let fileName = OV.NameFromLine (line, keyword.length, '#');
|
||||
let fileContent = this.callbacks.getFileContent (fileName);
|
||||
if (fileContent !== null) {
|
||||
OV.ReadLines (fileContent, function (line) {
|
||||
let fileBuffer = this.GetFileBuffer (fileName);
|
||||
if (fileBuffer !== null) {
|
||||
OV.ReadLines (fileBuffer, function (line) {
|
||||
if (!obj.IsError ()) {
|
||||
obj.ProcessLine (line);
|
||||
}
|
||||
@ -219,19 +221,19 @@ OV.ImporterObj = class extends OV.ImporterBase
|
||||
if (this.currentMaterial === null || parameters.length === 0) {
|
||||
return true;
|
||||
}
|
||||
this.currentMaterial.diffuseMap = CreateTexture (keyword, line, this.callbacks);
|
||||
this.currentMaterial.diffuseMap = CreateTexture (this, keyword, line);
|
||||
return true;
|
||||
} else if (keyword === 'map_ks') {
|
||||
if (this.currentMaterial === null || parameters.length === 0) {
|
||||
return true;
|
||||
}
|
||||
this.currentMaterial.specularMap = CreateTexture (keyword, line, this.callbacks);
|
||||
this.currentMaterial.specularMap = CreateTexture (this, keyword, line);
|
||||
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);
|
||||
this.currentMaterial.bumpMap = CreateTexture (this, keyword, line);
|
||||
return true;
|
||||
} else if (keyword === 'ka') {
|
||||
if (this.currentMaterial === null || parameters.length < 3) {
|
||||
|
||||
@ -24,6 +24,12 @@ OV.AsciiStringToArrayBuffer = function (str)
|
||||
return buffer;
|
||||
};
|
||||
|
||||
OV.Utf8StringToArrayBuffer = function (str)
|
||||
{
|
||||
let encoder = new TextEncoder ();
|
||||
return encoder.encode (str);
|
||||
};
|
||||
|
||||
OV.Base64DataURIToArrayBuffer = function (uri)
|
||||
{
|
||||
let dataPrefix = 'data:';
|
||||
@ -54,3 +60,22 @@ OV.Base64DataURIToArrayBuffer = function (uri)
|
||||
buffer : buffer
|
||||
};
|
||||
};
|
||||
|
||||
OV.CreateObjectUrl = function (content)
|
||||
{
|
||||
let blob = new Blob ([content]);
|
||||
let url = URL.createObjectURL (blob);
|
||||
return url;
|
||||
};
|
||||
|
||||
OV.CreateObjectUrlWithMimeType = function (content, mimeType)
|
||||
{
|
||||
let blob = new Blob ([content], { type : mimeType });
|
||||
let url = URL.createObjectURL (blob);
|
||||
return url;
|
||||
};
|
||||
|
||||
OV.RevokeObjectUrl = function (url)
|
||||
{
|
||||
URL.revokeObjectURL (url);
|
||||
};
|
||||
|
||||
@ -9,6 +9,22 @@ OV.MeshPrimitiveBuffer = class
|
||||
this.material = null;
|
||||
}
|
||||
|
||||
GetBounds ()
|
||||
{
|
||||
let min = [Infinity, Infinity, Infinity];
|
||||
let max = [-Infinity, -Infinity, -Infinity];
|
||||
for (let i = 0; i < this.vertices.length / 3; i++) {
|
||||
for (let j = 0; j < 3; j++) {
|
||||
min[j] = Math.min (min[j], this.vertices[i * 3 + j]);
|
||||
max[j] = Math.max (max[j], this.vertices[i * 3 + j]);
|
||||
}
|
||||
}
|
||||
return {
|
||||
min : min,
|
||||
max : max
|
||||
};
|
||||
}
|
||||
|
||||
GetByteLength (indexTypeSize, numberTypeSize)
|
||||
{
|
||||
let indexCount = this.indices.length;
|
||||
|
||||
@ -54,11 +54,17 @@ OV.TextureMap = class
|
||||
{
|
||||
this.name = null;
|
||||
this.url = null;
|
||||
this.buffer = null;
|
||||
this.offset = new OV.Coord2D (0.0, 0.0);
|
||||
this.scale = new OV.Coord2D (1.0, 1.0);
|
||||
this.rotation = 0.0; // radians
|
||||
}
|
||||
|
||||
IsValid ()
|
||||
{
|
||||
return this.name !== null && this.url !== null && this.buffer !== null;
|
||||
}
|
||||
|
||||
HasTransformation ()
|
||||
{
|
||||
if (!OV.CoordIsEqual2D (this.offset, new OV.Coord2D (0.0, 0.0))) {
|
||||
@ -78,6 +84,7 @@ OV.TextureMap = class
|
||||
let cloned = new OV.TextureMap ();
|
||||
cloned.name = this.name;
|
||||
cloned.url = this.url;
|
||||
cloned.buffer = this.buffer;
|
||||
cloned.offset = this.offset.Clone ();
|
||||
cloned.scale = this.scale.Clone ();
|
||||
cloned.rotation = this.rotation;
|
||||
@ -110,6 +117,25 @@ OV.Material = class
|
||||
this.multiplyDiffuseMap = false;
|
||||
}
|
||||
|
||||
EnumerateTextureMaps (enumerator)
|
||||
{
|
||||
if (this.diffuseMap !== null) {
|
||||
enumerator (this.diffuseMap);
|
||||
}
|
||||
if (this.specularMap !== null) {
|
||||
enumerator (this.specularMap);
|
||||
}
|
||||
if (this.bumpMap !== null) {
|
||||
enumerator (this.bumpMap);
|
||||
}
|
||||
if (this.normalMap !== null) {
|
||||
enumerator (this.normalMap);
|
||||
}
|
||||
if (this.emissiveMap !== null) {
|
||||
enumerator (this.emissiveMap);
|
||||
}
|
||||
}
|
||||
|
||||
Clone ()
|
||||
{
|
||||
let cloned = new OV.Material ();
|
||||
|
||||
@ -42,7 +42,7 @@ OV.Init3DViewerElements = function ()
|
||||
onTextureLoaded : function () {
|
||||
viewer.Render ();
|
||||
},
|
||||
onLoadError : function (importerError) {
|
||||
onLoadError : function (importError) {
|
||||
progressDiv.innerHTML = 'Unknown error.';
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"mocha": "*"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha"
|
||||
}
|
||||
}
|
||||
15
test/test.js
15
test/test.js
@ -1,20 +1,7 @@
|
||||
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/globals.js');
|
||||
require ('./utils/importall.js');
|
||||
var testDirName = './tests/';
|
||||
var files = fs.readdirSync (testDirName, { withFileTypes: true });
|
||||
|
||||
1
test/testfiles/3ds/wrong.3ds
Normal file
1
test/testfiles/3ds/wrong.3ds
Normal file
@ -0,0 +1 @@
|
||||
invalid 3ds file
|
||||
5
test/testfiles/obj/rectangle_with_texture.mtl
Normal file
5
test/testfiles/obj/rectangle_with_texture.mtl
Normal file
@ -0,0 +1,5 @@
|
||||
newmtl Material
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 1.000000 1.000000 1.000000
|
||||
Ks 0.000000 0.000000 0.000000
|
||||
map_Kd texture.png
|
||||
15
test/testfiles/obj/rectangle_with_texture.obj
Normal file
15
test/testfiles/obj/rectangle_with_texture.obj
Normal file
@ -0,0 +1,15 @@
|
||||
mtllib rectangle_with_texture.mtl
|
||||
|
||||
v 0.0 0.0 0.0
|
||||
v 1.0 0.0 0.0
|
||||
v 1.0 1.0 0.0
|
||||
v 0.0 1.0 0.0
|
||||
vn 0.0 0.0 1.0
|
||||
vt 0.0 0.0
|
||||
vt 1.0 0.0
|
||||
vt 1.0 1.0
|
||||
vt 0.0 1.0
|
||||
|
||||
o Mesh
|
||||
usemtl Material
|
||||
f 1/1/1 2/2/1 3/3/1 4/4/1
|
||||
BIN
test/testfiles/obj/texture.png
Normal file
BIN
test/testfiles/obj/texture.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
11
test/testfiles/obj/two_materials_same_texture.mtl
Normal file
11
test/testfiles/obj/two_materials_same_texture.mtl
Normal file
@ -0,0 +1,11 @@
|
||||
newmtl Material1
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 1.000000 1.000000 1.000000
|
||||
Ks 0.000000 0.000000 0.000000
|
||||
map_Kd ./texture.png
|
||||
|
||||
newmtl Material2
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 1.000000 1.000000 1.000000
|
||||
Ks 0.000000 0.000000 0.000000
|
||||
map_Kd texture.png
|
||||
17
test/testfiles/obj/two_materials_same_texture.obj
Normal file
17
test/testfiles/obj/two_materials_same_texture.obj
Normal file
@ -0,0 +1,17 @@
|
||||
mtllib two_materials_same_texture.mtl
|
||||
|
||||
v 0.0 0.0 0.0
|
||||
v 1.0 0.0 0.0
|
||||
v 1.0 1.0 0.0
|
||||
v 0.0 1.0 0.0
|
||||
vn 0.0 0.0 1.0
|
||||
vt 0.0 0.0
|
||||
vt 1.0 0.0
|
||||
vt 1.0 1.0
|
||||
vt 0.0 1.0
|
||||
|
||||
o Mesh
|
||||
usemtl Material1
|
||||
f 1/1/1 2/2/1 3/3/1
|
||||
usemtl Material2
|
||||
f 1/1/1 3/3/1 4/4/1
|
||||
1
test/testfiles/wrong.ext
Normal file
1
test/testfiles/wrong.ext
Normal file
@ -0,0 +1 @@
|
||||
file with not importable extension
|
||||
@ -12,12 +12,15 @@ function CreateTestModel ()
|
||||
material1.diffuseMap = new OV.TextureMap ();
|
||||
material1.diffuseMap.name = 'textures/texture1.png';
|
||||
material1.diffuseMap.url = 'texture1_url';
|
||||
material1.diffuseMap.buffer = new ArrayBuffer (4);
|
||||
material1.specularMap = new OV.TextureMap ();
|
||||
material1.specularMap.name = 'textures/texture2.png';
|
||||
material1.specularMap.url = 'texture2_url';
|
||||
material1.specularMap.buffer = new ArrayBuffer (4);
|
||||
material1.bumpMap = new OV.TextureMap ();
|
||||
material1.bumpMap.name = 'textures/texture3.png';
|
||||
material1.bumpMap.url = 'texture3_url';
|
||||
material1.bumpMap.buffer = new ArrayBuffer (4);
|
||||
model.AddMaterial (material1);
|
||||
|
||||
let material2 = new OV.Material ();
|
||||
@ -64,55 +67,57 @@ describe ('Exporter', function () {
|
||||
let model = CreateTestModel ();
|
||||
let exporter = new OV.Exporter ();
|
||||
|
||||
let result = exporter.Export (model, OV.FileFormat.Text, 'obj', null);
|
||||
let result = exporter.Export (model, OV.FileFormat.Text, 'obj');
|
||||
assert.strictEqual (result.length, 5);
|
||||
|
||||
let mtlFile = result[0];
|
||||
assert.strictEqual (mtlFile.GetName (), 'model.mtl');
|
||||
assert.strictEqual (mtlFile.GetContent (),
|
||||
`newmtl TestMaterial1
|
||||
Ka 0 0 0
|
||||
Kd 1 0 0
|
||||
Ks 0.2 0.2 0.2
|
||||
Ns 0
|
||||
d 1
|
||||
map_Kd texture1.png
|
||||
map_Ks texture2.png
|
||||
bump texture3.png
|
||||
newmtl TestMaterial2
|
||||
Ka 0 0 0
|
||||
Kd 0 1 0
|
||||
Ks 0.2 0.2 0.2
|
||||
Ns 0
|
||||
d 1
|
||||
`
|
||||
);
|
||||
[
|
||||
'newmtl TestMaterial1',
|
||||
'Ka 0 0 0',
|
||||
'Kd 1 0 0',
|
||||
'Ks 0.2 0.2 0.2',
|
||||
'Ns 0',
|
||||
'd 1',
|
||||
'map_Kd texture1.png',
|
||||
'map_Ks texture2.png',
|
||||
'bump texture3.png',
|
||||
'newmtl TestMaterial2',
|
||||
'Ka 0 0 0',
|
||||
'Kd 0 1 0',
|
||||
'Ks 0.2 0.2 0.2',
|
||||
'Ns 0',
|
||||
'd 1',
|
||||
''
|
||||
].join ('\n'));
|
||||
let objFile = result[1];
|
||||
assert.strictEqual (objFile.GetName (), 'model.obj');
|
||||
assert.strictEqual (objFile.GetContent (),
|
||||
`mtllib model.mtl
|
||||
g TestMesh1
|
||||
v 0 0 1
|
||||
v 1 0 1
|
||||
v 0 1 1
|
||||
vn 0 0 1
|
||||
usemtl TestMaterial1
|
||||
f 1//1 2//1 3//1
|
||||
g TestMesh2
|
||||
v 0 0 0
|
||||
v 1 0 0
|
||||
v 0 1 0
|
||||
v -1 0 0
|
||||
v 0 0 1
|
||||
vn 0 0 1
|
||||
vn 0 0 1
|
||||
vn -1 0 0
|
||||
f 4//2 5//2 6//2
|
||||
usemtl TestMaterial2
|
||||
f 4//3 6//3 7//3
|
||||
f 4//4 8//4 6//4
|
||||
`
|
||||
);
|
||||
[
|
||||
'mtllib model.mtl',
|
||||
'g TestMesh1',
|
||||
'v 0 0 1',
|
||||
'v 1 0 1',
|
||||
'v 0 1 1',
|
||||
'vn 0 0 1',
|
||||
'usemtl TestMaterial1',
|
||||
'f 1//1 2//1 3//1',
|
||||
'g TestMesh2',
|
||||
'v 0 0 0',
|
||||
'v 1 0 0',
|
||||
'v 0 1 0',
|
||||
'v -1 0 0',
|
||||
'v 0 0 1',
|
||||
'vn 0 0 1',
|
||||
'vn 0 0 1',
|
||||
'vn -1 0 0',
|
||||
'f 4//2 5//2 6//2',
|
||||
'usemtl TestMaterial2',
|
||||
'f 4//3 6//3 7//3',
|
||||
'f 4//4 8//4 6//4',
|
||||
''
|
||||
].join ('\n'));
|
||||
|
||||
let textureFile1 = result[2];
|
||||
assert.strictEqual (textureFile1.GetName (), 'texture1.png');
|
||||
@ -134,51 +139,52 @@ f 4//4 8//4 6//4
|
||||
let model = CreateTestModel ();
|
||||
let exporter = new OV.Exporter ();
|
||||
|
||||
let result = exporter.Export (model, OV.FileFormat.Text, 'stl', null);
|
||||
let result = exporter.Export (model, OV.FileFormat.Text, 'stl');
|
||||
assert.strictEqual (result.length, 1);
|
||||
|
||||
let stlFile = result[0];
|
||||
assert.strictEqual (stlFile.GetName (), 'model.stl');
|
||||
assert.strictEqual (stlFile.GetContent (),
|
||||
`solid Model
|
||||
facet normal 0 0 1
|
||||
outer loop
|
||||
vertex 0 0 1
|
||||
vertex 1 0 1
|
||||
vertex 0 1 1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 1
|
||||
outer loop
|
||||
vertex 0 0 0
|
||||
vertex 1 0 0
|
||||
vertex 0 1 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 1
|
||||
outer loop
|
||||
vertex 0 0 0
|
||||
vertex 0 1 0
|
||||
vertex -1 0 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -1 0 0
|
||||
outer loop
|
||||
vertex 0 0 0
|
||||
vertex 0 0 1
|
||||
vertex 0 1 0
|
||||
endloop
|
||||
endfacet
|
||||
endsolid Model
|
||||
`
|
||||
);
|
||||
});
|
||||
[
|
||||
'solid Model',
|
||||
'facet normal 0 0 1',
|
||||
' outer loop',
|
||||
' vertex 0 0 1',
|
||||
' vertex 1 0 1',
|
||||
' vertex 0 1 1',
|
||||
' endloop',
|
||||
'endfacet',
|
||||
'facet normal 0 0 1',
|
||||
' outer loop',
|
||||
' vertex 0 0 0',
|
||||
' vertex 1 0 0',
|
||||
' vertex 0 1 0',
|
||||
' endloop',
|
||||
'endfacet',
|
||||
'facet normal 0 0 1',
|
||||
' outer loop',
|
||||
' vertex 0 0 0',
|
||||
' vertex 0 1 0',
|
||||
' vertex -1 0 0',
|
||||
' endloop',
|
||||
'endfacet',
|
||||
'facet normal -1 0 0',
|
||||
' outer loop',
|
||||
' vertex 0 0 0',
|
||||
' vertex 0 0 1',
|
||||
' vertex 0 1 0',
|
||||
' endloop',
|
||||
'endfacet',
|
||||
'endsolid Model',
|
||||
''
|
||||
].join ('\n'));
|
||||
});
|
||||
|
||||
it ('Stl Binary Export', function () {
|
||||
let model = CreateTestModel ();
|
||||
let exporter = new OV.Exporter ();
|
||||
|
||||
let result = exporter.Export (model, OV.FileFormat.Binary, 'stl', null);
|
||||
let result = exporter.Export (model, OV.FileFormat.Binary, 'stl');
|
||||
assert.strictEqual (result.length, 1);
|
||||
|
||||
let stlFile = result[0];
|
||||
@ -195,76 +201,78 @@ endsolid Model
|
||||
let importedModel = importer.GetModel ();
|
||||
assert.strictEqual (importedModel.VertexCount (), 12);
|
||||
assert.strictEqual (importedModel.TriangleCount (), 4);
|
||||
});
|
||||
});
|
||||
|
||||
it ('Off Export', function () {
|
||||
let model = CreateTestModel ();
|
||||
let exporter = new OV.Exporter ();
|
||||
let model = CreateTestModel ();
|
||||
let exporter = new OV.Exporter ();
|
||||
|
||||
let result = exporter.Export (model, OV.FileFormat.Text, 'off', null);
|
||||
assert.strictEqual (result.length, 1);
|
||||
let result = exporter.Export (model, OV.FileFormat.Text, 'off');
|
||||
assert.strictEqual (result.length, 1);
|
||||
|
||||
let offFile = result[0];
|
||||
assert.strictEqual (offFile.GetName (), 'model.off');
|
||||
assert.strictEqual (offFile.GetContent (),
|
||||
`OFF
|
||||
8 4 0
|
||||
0 0 1
|
||||
1 0 1
|
||||
0 1 1
|
||||
0 0 0
|
||||
1 0 0
|
||||
0 1 0
|
||||
-1 0 0
|
||||
0 0 1
|
||||
3 0 1 2
|
||||
3 3 4 5
|
||||
3 3 5 6
|
||||
3 3 7 5
|
||||
`
|
||||
);
|
||||
let offFile = result[0];
|
||||
assert.strictEqual (offFile.GetName (), 'model.off');
|
||||
assert.strictEqual (offFile.GetContent (),
|
||||
[
|
||||
'OFF',
|
||||
'8 4 0',
|
||||
'0 0 1',
|
||||
'1 0 1',
|
||||
'0 1 1',
|
||||
'0 0 0',
|
||||
'1 0 0',
|
||||
'0 1 0',
|
||||
'-1 0 0',
|
||||
'0 0 1',
|
||||
'3 0 1 2',
|
||||
'3 3 4 5',
|
||||
'3 3 5 6',
|
||||
'3 3 7 5',
|
||||
''
|
||||
].join ('\n'));
|
||||
});
|
||||
|
||||
it ('Ply Export', function () {
|
||||
let model = CreateTestModel ();
|
||||
let exporter = new OV.Exporter ();
|
||||
let model = CreateTestModel ();
|
||||
let exporter = new OV.Exporter ();
|
||||
|
||||
let result = exporter.Export (model, OV.FileFormat.Text, 'ply', null);
|
||||
assert.strictEqual (result.length, 1);
|
||||
let result = exporter.Export (model, OV.FileFormat.Text, 'ply');
|
||||
assert.strictEqual (result.length, 1);
|
||||
|
||||
let plyFile = result[0];
|
||||
assert.strictEqual (plyFile.GetName (), 'model.ply');
|
||||
assert.strictEqual (plyFile.GetContent (),
|
||||
`ply
|
||||
format ascii 1.0
|
||||
element vertex 8
|
||||
property float x
|
||||
property float y
|
||||
property float z
|
||||
element face 4
|
||||
property list uchar int vertex_index
|
||||
end_header
|
||||
0 0 1
|
||||
1 0 1
|
||||
0 1 1
|
||||
0 0 0
|
||||
1 0 0
|
||||
0 1 0
|
||||
-1 0 0
|
||||
0 0 1
|
||||
3 0 1 2
|
||||
3 3 4 5
|
||||
3 3 5 6
|
||||
3 3 7 5
|
||||
`
|
||||
);
|
||||
let plyFile = result[0];
|
||||
assert.strictEqual (plyFile.GetName (), 'model.ply');
|
||||
assert.strictEqual (plyFile.GetContent (),
|
||||
[
|
||||
'ply',
|
||||
'format ascii 1.0',
|
||||
'element vertex 8',
|
||||
'property float x',
|
||||
'property float y',
|
||||
'property float z',
|
||||
'element face 4',
|
||||
'property list uchar int vertex_index',
|
||||
'end_header',
|
||||
'0 0 1',
|
||||
'1 0 1',
|
||||
'0 1 1',
|
||||
'0 0 0',
|
||||
'1 0 0',
|
||||
'0 1 0',
|
||||
'-1 0 0',
|
||||
'0 0 1',
|
||||
'3 0 1 2',
|
||||
'3 3 4 5',
|
||||
'3 3 5 6',
|
||||
'3 3 7 5',
|
||||
''
|
||||
].join ('\n'));
|
||||
});
|
||||
|
||||
it ('Ply Binary Export', function () {
|
||||
let model = CreateTestModel ();
|
||||
let exporter = new OV.Exporter ();
|
||||
|
||||
let result = exporter.Export (model, OV.FileFormat.Binary, 'ply', null);
|
||||
let result = exporter.Export (model, OV.FileFormat.Binary, 'ply');
|
||||
assert.strictEqual (result.length, 1);
|
||||
|
||||
let plyFile = result[0];
|
||||
@ -284,55 +292,51 @@ end_header
|
||||
});
|
||||
|
||||
it ('Gltf Ascii Export', function () {
|
||||
let model = CreateTestModel ();
|
||||
let exporter = new OV.Exporter ();
|
||||
|
||||
let result = exporter.Export (model, OV.FileFormat.Text, 'gltf', null);
|
||||
assert.strictEqual (result.length, 3);
|
||||
|
||||
let gltfFile = result[0];
|
||||
let binFile = result[1];
|
||||
let textureFile = result[2];
|
||||
assert.strictEqual (gltfFile.GetName (), 'model.gltf');
|
||||
assert.strictEqual (binFile.GetName (), 'model.bin');
|
||||
|
||||
assert.strictEqual (textureFile.GetName (), 'texture1.png');
|
||||
assert.strictEqual (textureFile.GetUrl (), 'texture1_url');
|
||||
assert.strictEqual (textureFile.GetContent (), null);
|
||||
|
||||
let contentBuffer = gltfFile.GetContent ();
|
||||
let importer = new OV.ImporterGltf ();
|
||||
importer.Import (contentBuffer, 'gltf', {
|
||||
getDefaultMaterial () {
|
||||
return new OV.Material ();
|
||||
},
|
||||
getFileContent (filePath) {
|
||||
return binFile.GetContent ();
|
||||
},
|
||||
getTextureObjectUrl (filePath) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
let importedModel = importer.GetModel ();
|
||||
assert (OV.CheckModel (importedModel));
|
||||
assert.strictEqual (importedModel.MaterialCount (), 2);
|
||||
assert.strictEqual (importedModel.MeshCount (), 2);
|
||||
assert.strictEqual (importedModel.GetMesh (0).GetName (), 'TestMesh1');
|
||||
assert.strictEqual (importedModel.GetMesh (1).GetName (), 'TestMesh2');
|
||||
assert.strictEqual (importedModel.VertexCount (), 12);
|
||||
assert.strictEqual (importedModel.TriangleCount (), 4);
|
||||
let model = CreateTestModel ();
|
||||
let exporter = new OV.Exporter ();
|
||||
|
||||
let result = exporter.Export (model, OV.FileFormat.Text, 'gltf');
|
||||
assert.strictEqual (result.length, 3);
|
||||
|
||||
let gltfFile = result[0];
|
||||
let binFile = result[1];
|
||||
let textureFile = result[2];
|
||||
assert.strictEqual (gltfFile.GetName (), 'model.gltf');
|
||||
assert.strictEqual (binFile.GetName (), 'model.bin');
|
||||
|
||||
assert.strictEqual (textureFile.GetName (), 'texture1.png');
|
||||
assert.strictEqual (textureFile.GetUrl (), 'texture1_url');
|
||||
assert.strictEqual (textureFile.GetContent (), null);
|
||||
|
||||
let contentBuffer = gltfFile.GetContent ();
|
||||
let importer = new OV.ImporterGltf ();
|
||||
importer.Import (contentBuffer, 'gltf', {
|
||||
getDefaultMaterial () {
|
||||
return new OV.Material ();
|
||||
},
|
||||
getFileBuffer (filePath) {
|
||||
if (filePath == 'model.bin') {
|
||||
return binFile.GetContent ();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
let importedModel = importer.GetModel ();
|
||||
assert (OV.CheckModel (importedModel));
|
||||
assert.strictEqual (importedModel.MaterialCount (), 2);
|
||||
assert.strictEqual (importedModel.MeshCount (), 2);
|
||||
assert.strictEqual (importedModel.GetMesh (0).GetName (), 'TestMesh1');
|
||||
assert.strictEqual (importedModel.GetMesh (1).GetName (), 'TestMesh2');
|
||||
assert.strictEqual (importedModel.VertexCount (), 12);
|
||||
assert.strictEqual (importedModel.TriangleCount (), 4);
|
||||
});
|
||||
|
||||
it ('Gltf Binary Export', function () {
|
||||
let model = CreateTestModel ();
|
||||
let exporter = new OV.Exporter ();
|
||||
|
||||
let result = exporter.Export (model, OV.FileFormat.Binary, 'glb', {
|
||||
getTextureBuffer : function (name) {
|
||||
return new ArrayBuffer (4);
|
||||
}
|
||||
});
|
||||
let result = exporter.Export (model, OV.FileFormat.Binary, 'glb');
|
||||
assert.strictEqual (result.length, 1);
|
||||
|
||||
let glbFile = result[0];
|
||||
@ -344,10 +348,7 @@ end_header
|
||||
getDefaultMaterial () {
|
||||
return new OV.Material ();
|
||||
},
|
||||
getFileContent (filePath) {
|
||||
return binFile.GetContent ();
|
||||
},
|
||||
getTextureObjectUrl (filePath) {
|
||||
getFileBuffer (filePath) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
182
test/tests/importer_test.js
Normal file
182
test/tests/importer_test.js
Normal file
@ -0,0 +1,182 @@
|
||||
var assert = require ('assert');
|
||||
var path = require ('path');
|
||||
|
||||
function ImportFilesWithImporter (importer, files, callbacks)
|
||||
{
|
||||
importer.LoadFilesFromFileObjects (files, function () {
|
||||
importer.Import ({
|
||||
success : function (importResult) {
|
||||
callbacks.success (importer, importResult);
|
||||
},
|
||||
error : function (importError) {
|
||||
callbacks.error (importer, importError);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function ImportFiles (files, callbacks)
|
||||
{
|
||||
let importer = new OV.Importer ();
|
||||
ImportFilesWithImporter (importer, files, callbacks);
|
||||
}
|
||||
|
||||
describe ('Importer Test', function () {
|
||||
it ('Empty File List', function () {
|
||||
let files = [];
|
||||
ImportFiles (files, {
|
||||
success : function (importer, importResult) {
|
||||
assert.fail ();
|
||||
},
|
||||
error : function (importer, importError) {
|
||||
assert.strictEqual (importError.code, OV.ImportErrorCode.NoImportableFile);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it ('Not existing file', function () {
|
||||
let files = [
|
||||
new FileObject ('obj', 'missing.obj')
|
||||
];
|
||||
ImportFiles (files, {
|
||||
success : function (importer, importResult) {
|
||||
assert.fail ();
|
||||
},
|
||||
error : function (importer, importError) {
|
||||
assert.strictEqual (importError.code, OV.ImportErrorCode.NoImportableFile);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it ('Not imprtable file', function () {
|
||||
let files = [
|
||||
new FileObject ('', 'wrong.ext')
|
||||
];
|
||||
ImportFiles (files, {
|
||||
success : function (importer, importResult) {
|
||||
assert.fail ();
|
||||
},
|
||||
error : function (importer, importError) {
|
||||
assert.strictEqual (importError.code, OV.ImportErrorCode.NoImportableFile);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it ('Wrong file', function () {
|
||||
let files = [
|
||||
new FileObject ('3ds', 'wrong.3ds')
|
||||
];
|
||||
ImportFiles (files, {
|
||||
success : function (importer, importResult) {
|
||||
assert.fail ();
|
||||
},
|
||||
error : function (importer, importError) {
|
||||
assert.strictEqual (importError.code, OV.ImportErrorCode.ImportFailed);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it ('Single file', function () {
|
||||
let files = [
|
||||
new FileObject ('obj', 'single_triangle.obj')
|
||||
];
|
||||
ImportFiles (files, {
|
||||
success : function (importer, importResult) {
|
||||
assert (!OV.IsModelEmpty (importResult.model));
|
||||
assert.deepStrictEqual (importResult.usedFiles, ['single_triangle.obj']);
|
||||
assert.deepStrictEqual (importResult.missingFiles, []);
|
||||
},
|
||||
error : function (importer, importError) {
|
||||
assert.fail ();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it ('Missing files', function () {
|
||||
let files = [];
|
||||
files.push (new FileObject ('obj', 'cube_with_materials.obj'));
|
||||
ImportFiles (files, {
|
||||
success : function (importer, importResult) {
|
||||
assert (!OV.IsModelEmpty (importResult.model));
|
||||
assert.deepStrictEqual (importResult.usedFiles, ['cube_with_materials.obj']);
|
||||
assert.deepStrictEqual (importResult.missingFiles, ['cube_with_materials.mtl']);
|
||||
},
|
||||
error : function (importer, importError) {
|
||||
assert.fail ();
|
||||
}
|
||||
});
|
||||
files.push (new FileObject ('obj', 'cube_with_materials.mtl'));
|
||||
ImportFiles (files, {
|
||||
success : function (importer, importResult) {
|
||||
assert (!OV.IsModelEmpty (importResult.model));
|
||||
assert.deepStrictEqual (importResult.usedFiles, ['cube_with_materials.obj', 'cube_with_materials.mtl']);
|
||||
assert.deepStrictEqual (importResult.missingFiles, ['cube_texture.png']);
|
||||
},
|
||||
error : function (importer, importError) {
|
||||
assert.fail ();
|
||||
}
|
||||
});
|
||||
files.push (new FileObject ('obj', 'cube_texture.png'));
|
||||
ImportFiles (files, {
|
||||
success : function (importer, importResult) {
|
||||
assert (!OV.IsModelEmpty (importResult.model));
|
||||
assert.deepStrictEqual (importResult.usedFiles, ['cube_with_materials.obj', 'cube_with_materials.mtl', 'cube_texture.png']);
|
||||
assert.deepStrictEqual (importResult.missingFiles, []);
|
||||
},
|
||||
error : function (importer, importError) {
|
||||
assert.fail ();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it ('Missing texture multiple times', function () {
|
||||
let files = [
|
||||
new FileObject ('obj', 'two_materials_same_texture.obj'),
|
||||
new FileObject ('obj', 'two_materials_same_texture.mtl'),
|
||||
];
|
||||
ImportFiles (files, {
|
||||
success : function (importer, importResult) {
|
||||
assert (!OV.IsModelEmpty (importResult.model));
|
||||
assert.deepStrictEqual (importResult.usedFiles, ['two_materials_same_texture.obj', 'two_materials_same_texture.mtl']);
|
||||
assert.deepStrictEqual (importResult.missingFiles, ['texture.png']);
|
||||
},
|
||||
error : function (importer, importError) {
|
||||
assert.fail ();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it ('Append Missing files', function () {
|
||||
let theImporter = new OV.Importer ();
|
||||
ImportFilesWithImporter (theImporter, [new FileObject ('obj', 'cube_with_materials.obj')], {
|
||||
success : function (importer, importResult) {
|
||||
assert (!OV.IsModelEmpty (importResult.model));
|
||||
assert.deepStrictEqual (importResult.usedFiles, ['cube_with_materials.obj']);
|
||||
assert.deepStrictEqual (importResult.missingFiles, ['cube_with_materials.mtl']);
|
||||
ImportFilesWithImporter (theImporter, [new FileObject ('obj', 'cube_with_materials.mtl')], {
|
||||
success : function (importer, importResult) {
|
||||
assert (!OV.IsModelEmpty (importResult.model));
|
||||
assert.deepStrictEqual (importResult.usedFiles, ['cube_with_materials.obj', 'cube_with_materials.mtl']);
|
||||
assert.deepStrictEqual (importResult.missingFiles, ['cube_texture.png']);
|
||||
ImportFilesWithImporter (theImporter, [new FileObject ('obj', 'cube_texture.png')], {
|
||||
success : function (importer, importResult) {
|
||||
assert (!OV.IsModelEmpty (importResult.model));
|
||||
assert.deepStrictEqual (importResult.usedFiles, ['cube_with_materials.obj', 'cube_with_materials.mtl', 'cube_texture.png']);
|
||||
assert.deepStrictEqual (importResult.missingFiles, []);
|
||||
},
|
||||
error : function (importer, importError) {
|
||||
assert.fail ();
|
||||
}
|
||||
});
|
||||
},
|
||||
error : function (importer, importError) {
|
||||
assert.fail ();
|
||||
}
|
||||
});
|
||||
},
|
||||
error : function (importer, importError) {
|
||||
assert.fail ();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -374,6 +374,69 @@ describe ('Obj Importer', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it ('rectangle_with_texture.obj', function () {
|
||||
var model = testFiles.ImportObjFile ('rectangle_with_texture.obj');
|
||||
assert (OV.CheckModel (model));
|
||||
assert.deepStrictEqual (testUtils.ModelToObject (model), {
|
||||
name : '',
|
||||
materials : [
|
||||
{ name : 'Material' }
|
||||
],
|
||||
meshes : [
|
||||
{
|
||||
name : 'Mesh',
|
||||
triangles : [
|
||||
{
|
||||
vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0],
|
||||
normals : [0, 0, 1, 0, 0, 1, 0, 0, 1],
|
||||
uvs : [0, 0, 1, 0, 1, 1],
|
||||
mat : 0
|
||||
},
|
||||
{
|
||||
vertices : [0, 0, 0, 1, 1, 0, 0, 1, 0],
|
||||
normals : [0, 0, 1, 0, 0, 1, 0, 0, 1],
|
||||
uvs : [0, 0, 1, 1, 0, 1],
|
||||
mat : 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it ('two_materials_same_texture.obj', function () {
|
||||
var model = testFiles.ImportObjFile ('two_materials_same_texture.obj');
|
||||
assert.strictEqual (model.GetMaterial (0).diffuseMap.url, model.GetMaterial (1).diffuseMap.url);
|
||||
assert.strictEqual (model.GetMaterial (0).diffuseMap.buffer.byteLength, model.GetMaterial (1).diffuseMap.buffer.byteLength);
|
||||
assert (OV.CheckModel (model));
|
||||
assert.deepStrictEqual (testUtils.ModelToObject (model), {
|
||||
name : '',
|
||||
materials : [
|
||||
{ name : 'Material1' },
|
||||
{ name : 'Material2' }
|
||||
],
|
||||
meshes : [
|
||||
{
|
||||
name : 'Mesh',
|
||||
triangles : [
|
||||
{
|
||||
vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0],
|
||||
normals : [0, 0, 1, 0, 0, 1, 0, 0, 1],
|
||||
uvs : [0, 0, 1, 0, 1, 1],
|
||||
mat : 0
|
||||
},
|
||||
{
|
||||
vertices : [0, 0, 0, 1, 1, 0, 0, 1, 0],
|
||||
normals : [0, 0, 1, 0, 0, 1, 0, 0, 1],
|
||||
uvs : [0, 0, 1, 1, 0, 1],
|
||||
mat : 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it ('cube_with_materials.obj', function () {
|
||||
var model = testFiles.ImportObjFile ('cube_with_materials.obj');
|
||||
assert (OV.CheckModel (model));
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
var assert = require ('assert');
|
||||
var testFiles = require ('../utils/testfiles.js');
|
||||
var testUtils = require ('../utils/testutils.js');
|
||||
|
||||
describe ('IO Test', function () {
|
||||
it ('Binary Reader', function () {
|
||||
let buffer = testFiles.GetArrayBufferFileContent ('bin', 'binary_content.bin');
|
||||
let buffer = testUtils.GetArrayBufferFileContent ('bin', 'binary_content.bin');
|
||||
let reader = new OV.BinaryReader (buffer, true);
|
||||
|
||||
assert.strictEqual (reader.GetByteLength (), 166);
|
||||
@ -89,4 +89,13 @@ describe ('IO Test', function () {
|
||||
assert.strictEqual (reader.ReadFloat32 (), 7.5);
|
||||
assert.strictEqual (reader.ReadDouble64 (), 8.5);
|
||||
});
|
||||
|
||||
it ('Utf8 Conversion', function () {
|
||||
let str = 'example-\u2764-example';
|
||||
let buffer = OV.Utf8StringToArrayBuffer (str);
|
||||
assert.strictEqual (buffer.byteLength, 19);
|
||||
let str2 = OV.ArrayBufferToUtf8String (buffer);
|
||||
assert.strictEqual (str, str2);
|
||||
assert.strictEqual (str.length, str2.length);
|
||||
});
|
||||
});
|
||||
|
||||
60
test/utils/globals.js
Normal file
60
test/utils/globals.js
Normal file
@ -0,0 +1,60 @@
|
||||
var path = require ('path');
|
||||
var testUtils = require ('./testutils.js');
|
||||
|
||||
global.atob = function (base64String) {
|
||||
return Buffer.from (base64String, 'base64').toString('binary');
|
||||
};
|
||||
|
||||
global.Blob = function () {
|
||||
|
||||
};
|
||||
|
||||
var objectUrlCounter = 0;
|
||||
global.URL = {
|
||||
createObjectURL : function () {
|
||||
objectUrlCounter += 1;
|
||||
return 'ObjectUrl:' + objectUrlCounter.toString ();
|
||||
}
|
||||
};
|
||||
|
||||
global.FileObject = function (folderName, fileName)
|
||||
{
|
||||
this.folderName = folderName;
|
||||
this.fileName = fileName;
|
||||
this.name = path.join (folderName, fileName);
|
||||
};
|
||||
|
||||
global.FileReader = class
|
||||
{
|
||||
static DONE = 2;
|
||||
|
||||
readAsText (fileObject)
|
||||
{
|
||||
let content = testUtils.GetTextFileContent (fileObject.folderName, fileObject.fileName);
|
||||
if (content !== null) {
|
||||
this.onloadend ({
|
||||
target : {
|
||||
readyState : FileReader.DONE,
|
||||
result : content
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.onerror ();
|
||||
}
|
||||
}
|
||||
|
||||
readAsArrayBuffer (fileObject)
|
||||
{
|
||||
let content = testUtils.GetArrayBufferFileContent (fileObject.folderName, fileObject.fileName);
|
||||
if (content !== null) {
|
||||
this.onloadend ({
|
||||
target : {
|
||||
readyState : FileReader.DONE,
|
||||
result : content
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.onerror ();
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1,28 +1,7 @@
|
||||
var path = require ('path');
|
||||
var fs = require ('fs');
|
||||
var testUtils = require ('./testutils.js')
|
||||
var testUtils = require ('./testutils.js');
|
||||
|
||||
module.exports =
|
||||
{
|
||||
GetTextFileContent : function (folder, fileName)
|
||||
{
|
||||
var testFilePath = path.join ('testfiles', folder, fileName);
|
||||
return fs.readFileSync (testFilePath).toString ();
|
||||
},
|
||||
|
||||
GetArrayBufferFileContent : function (folder, fileName)
|
||||
{
|
||||
var testFilePath = path.join ('testfiles', folder, fileName);
|
||||
var buffer = fs.readFileSync (testFilePath);
|
||||
var arrayBuffer = new ArrayBuffer (buffer.length);
|
||||
var uint8Array = new Uint8Array (arrayBuffer);
|
||||
var i;
|
||||
for (i = 0; i < buffer.length; ++i) {
|
||||
uint8Array[i] = buffer[i];
|
||||
}
|
||||
return arrayBuffer
|
||||
},
|
||||
|
||||
ImportObjFile : function (fileName)
|
||||
{
|
||||
var importer = new OV.ImporterObj ();
|
||||
@ -65,13 +44,30 @@ module.exports =
|
||||
},
|
||||
|
||||
ImportFile : function (importer, format, folder, fileName)
|
||||
{
|
||||
var myThis = this;
|
||||
{
|
||||
function GetFileBuffer (filePath, importer)
|
||||
{
|
||||
let extension = OV.GetFileExtension (filePath);
|
||||
let knownFormats = importer.GetKnownFileFormats ();
|
||||
let format = OV.FileFormat.Binary;
|
||||
if (knownFormats[extension] !== undefined) {
|
||||
format = knownFormats[extension];
|
||||
}
|
||||
let fileContent = null;
|
||||
if (format == OV.FileFormat.Text) {
|
||||
fileContent = testUtils.GetTextFileContent (folder, filePath);
|
||||
} else if (format == OV.FileFormat.Binary) {
|
||||
fileContent = testUtils.GetArrayBufferFileContent (folder, filePath);
|
||||
}
|
||||
return fileContent;
|
||||
}
|
||||
|
||||
var obj = this;
|
||||
var content = null;
|
||||
if (format == OV.FileFormat.Text) {
|
||||
content = this.GetTextFileContent (folder, fileName);
|
||||
content = testUtils.GetTextFileContent (folder, fileName);
|
||||
} else if (format == OV.FileFormat.Binary) {
|
||||
content = this.GetArrayBufferFileContent (folder, fileName);
|
||||
content = testUtils.GetArrayBufferFileContent (folder, fileName);
|
||||
}
|
||||
var extension = OV.GetFileExtension (fileName);
|
||||
importer.Import (content, extension, {
|
||||
@ -79,23 +75,8 @@ module.exports =
|
||||
var material = new OV.Material ();
|
||||
return material;
|
||||
},
|
||||
getFileContent : function (requestedFileName) {
|
||||
let extension = OV.GetFileExtension (requestedFileName);
|
||||
let knownFormats = importer.GetKnownFileFormats ();
|
||||
let format = OV.FileFormat.Binary;
|
||||
if (knownFormats[extension] !== undefined) {
|
||||
format = knownFormats[extension];
|
||||
}
|
||||
let fileContent = null;
|
||||
if (format == OV.FileFormat.Text) {
|
||||
fileContent = myThis.GetTextFileContent (folder, requestedFileName);
|
||||
} else if (format == OV.FileFormat.Binary) {
|
||||
fileContent = myThis.GetArrayBufferFileContent (folder, requestedFileName);
|
||||
}
|
||||
return fileContent;
|
||||
},
|
||||
getTextureObjectUrl : function (requestedFileName) {
|
||||
return requestedFileName;
|
||||
getFileBuffer : function (filePath) {
|
||||
return GetFileBuffer (filePath, importer);
|
||||
}
|
||||
});
|
||||
let model = importer.GetModel ();
|
||||
|
||||
@ -1,5 +1,30 @@
|
||||
var fs = require ('fs');
|
||||
var path = require ('path');
|
||||
|
||||
module.exports =
|
||||
{
|
||||
GetTextFileContent : function (folder, fileName)
|
||||
{
|
||||
var testFilePath = path.join ('testfiles', folder, fileName);
|
||||
if (!fs.existsSync (testFilePath)) {
|
||||
return null;
|
||||
}
|
||||
return fs.readFileSync (testFilePath).toString ();
|
||||
},
|
||||
|
||||
GetArrayBufferFileContent : function (folder, fileName)
|
||||
{
|
||||
var testFilePath = path.join ('testfiles', folder, fileName);
|
||||
var buffer = fs.readFileSync (testFilePath);
|
||||
var arrayBuffer = new ArrayBuffer (buffer.length);
|
||||
var uint8Array = new Uint8Array (arrayBuffer);
|
||||
var i;
|
||||
for (i = 0; i < buffer.length; ++i) {
|
||||
uint8Array[i] = buffer[i];
|
||||
}
|
||||
return arrayBuffer
|
||||
},
|
||||
|
||||
ModelToObject : function (model)
|
||||
{
|
||||
var obj = {
|
||||
|
||||
@ -37,7 +37,7 @@ def CompressFiles (inputFiles, outputFile):
|
||||
return False
|
||||
return True
|
||||
|
||||
def CreateDestinationDir (config, rootDir, websiteDir, version):
|
||||
def CreateDestinationDir (config, rootDir, websiteDir, version, testBuild):
|
||||
if not os.path.exists (websiteDir):
|
||||
os.makedirs (websiteDir)
|
||||
|
||||
@ -71,7 +71,7 @@ def CreateDestinationDir (config, rootDir, websiteDir, version):
|
||||
metaContent = Tools.GetFileContent (metaFile)
|
||||
replacer.ReplaceTokenContent ('<!-- meta start -->', '<!-- meta end -->', metaContent)
|
||||
analyticsFile = os.path.join (rootDir, 'tools', 'website_analytics_data.txt')
|
||||
if os.path.exists (analyticsFile):
|
||||
if os.path.exists (analyticsFile) and not testBuild:
|
||||
analyticsContent = Tools.GetFileContent (analyticsFile)
|
||||
replacer.ReplaceTokenContent ('<!-- analytics start -->', '<!-- analytics end -->', analyticsContent)
|
||||
scriptFile = os.path.join (rootDir, 'tools', 'website_script_data.txt')
|
||||
@ -86,8 +86,8 @@ def CreatePackage (websiteDir, packageDir, version):
|
||||
|
||||
zipPath = os.path.join (packageDir, 'o3dv_' + version + '.zip')
|
||||
zip = zipfile.ZipFile (zipPath, mode = 'w', compression = zipfile.ZIP_DEFLATED)
|
||||
zip.write (os.path.join (websiteDir, 'libs', 'three.min-126.js'), 'three.min.js')
|
||||
zip.write (os.path.join (websiteDir, 'o3dv', 'o3dv.min.js'), 'o3dv.min.js')
|
||||
zip.write (os.path.join (websiteDir, 'libs', 'three.min-126.js'), 'three.min-126.js')
|
||||
zip.write (os.path.join (websiteDir, 'o3dv', 'o3dv.min.js'), 'o3dv.min-' + version + '.js')
|
||||
zip.close ()
|
||||
return True
|
||||
|
||||
@ -96,12 +96,19 @@ def Main (argv):
|
||||
os.chdir (currentDir)
|
||||
|
||||
rootDir = os.path.abspath ('..')
|
||||
buildDir = os.path.join (rootDir, 'build')
|
||||
|
||||
testBuild = False
|
||||
buildDir = os.path.join (rootDir, 'build', 'final')
|
||||
if len (argv) >= 2 and argv[1] == 'test':
|
||||
testBuild = True
|
||||
buildDir = os.path.join (rootDir, 'build', 'test')
|
||||
PrintInfo ('Creating test build.')
|
||||
|
||||
websiteDir = os.path.join (buildDir, 'website')
|
||||
packageDir = os.path.join (buildDir, 'package')
|
||||
if os.path.exists (buildDir):
|
||||
shutil.rmtree (buildDir)
|
||||
|
||||
|
||||
config = None
|
||||
with open ('config.json') as configJson:
|
||||
config = json.load (configJson)
|
||||
@ -120,7 +127,7 @@ def Main (argv):
|
||||
|
||||
version = GetVersion (rootDir)
|
||||
PrintInfo ('Create build directory')
|
||||
CreateDestinationDir (config, rootDir, websiteDir, version)
|
||||
CreateDestinationDir (config, rootDir, websiteDir, version, testBuild)
|
||||
|
||||
PrintInfo ('Compress importer sources.')
|
||||
compressResult = CompressFiles (config['importer_files'], os.path.join (websiteDir, 'o3dv', 'o3dv.min.js'))
|
||||
|
||||
@ -11,22 +11,22 @@
|
||||
|
||||
<body>
|
||||
<iframe
|
||||
src="embed.html#model=../test/testfiles/3ds/cube_four_instances.3ds,../test/testfiles/3ds/texture.png"
|
||||
src="../../website/embed.html#model=../test/testfiles/3ds/cube_four_instances.3ds,../test/testfiles/3ds/texture.png"
|
||||
width=400 height=300
|
||||
style="border:1px solid #eeeeee;">
|
||||
</iframe>
|
||||
<iframe
|
||||
src="embed.html#model=../test/testfiles/3ds/cube_four_instances.3ds,../test/testfiles/3ds/texture.png$camera=3.929421317669367,6.153966358575169,2.7076091223424714,1.5,1.5,0.5,0,0,1"
|
||||
src="../../website/embed.html#model=../test/testfiles/3ds/cube_four_instances.3ds,../test/testfiles/3ds/texture.png$camera=3.929421317669367,6.153966358575169,2.7076091223424714,1.5,1.5,0.5,0,0,1"
|
||||
width=400 height=300
|
||||
style="border:1px solid #eeeeee;">
|
||||
</iframe>
|
||||
<iframe
|
||||
src="embed.html#model=../test/testfiles/obj/hundred_cubes.obj,../test/testfiles/obj/hundred_cubes.mtl"
|
||||
src="../../website/embed.html#model=../test/testfiles/obj/hundred_cubes.obj,../test/testfiles/obj/hundred_cubes.mtl"
|
||||
width=400 height=300
|
||||
style="border:1px solid #eeeeee;">
|
||||
</iframe>
|
||||
<iframe
|
||||
src="embed.html#model=wrong.3ds"
|
||||
src="../../website/embed.html#model=wrong.3ds"
|
||||
width=400 height=300
|
||||
style="border:1px solid #eeeeee;">
|
||||
</iframe>
|
||||
37
tools/sandbox/embed_selfhost.html
Normal file
37
tools/sandbox/embed_selfhost.html
Normal file
@ -0,0 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html;charset=utf-8">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no">
|
||||
<link rel="icon" type="image/png" href="assets/images/3dviewer_net_favicon.png">
|
||||
|
||||
<title>Online 3D Viewer</title>
|
||||
|
||||
<script type="text/javascript" src="../../libs/three.min-126.js"></script>
|
||||
<script type="text/javascript" src="../../build/final/website/o3dv/o3dv.min.js"></script>
|
||||
|
||||
<script type='text/javascript'>
|
||||
OV.Init3DViewerElements ();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
div.online_3d_viewer
|
||||
{
|
||||
float: left;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="online_3d_viewer" width="400" height="300" model="../../test/testfiles/3ds/cube_four_instances.3ds,../../test/testfiles/3ds/texture.png"></div>
|
||||
<div class="online_3d_viewer"
|
||||
width="400" height="300"
|
||||
model="../../test/testfiles/3ds/cube_four_instances.3ds,../../test/testfiles/3ds/texture.png"
|
||||
camera="-1.5,-3.0,2.0,0,0,0,0,0,1">
|
||||
</div>
|
||||
<div class="online_3d_viewer" width="400" height="300" model="../../test/testfiles/obj/hundred_cubes.obj,../../test/testfiles/obj/hundred_cubes.mtl"></div>
|
||||
<div class="online_3d_viewer" width="400" height="300" model="wrong.3ds"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@ -11,7 +11,7 @@
|
||||
<script type='text/javascript'>
|
||||
function Sandbox3D ()
|
||||
{
|
||||
let canvas = document.getElementById ('canvas')
|
||||
let canvas = document.getElementById ('canvas');
|
||||
|
||||
let parameters = {
|
||||
canvas : canvas,
|
||||
@ -6,7 +6,7 @@
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no">
|
||||
<link rel="icon" type="image/png" href="assets/images/3dviewer_net_favicon.png">
|
||||
|
||||
<title>Online 3D Viewer</title>
|
||||
<title>Online 3D Viewer Embedded</title>
|
||||
|
||||
<!-- meta start -->
|
||||
<!-- meta end -->
|
||||
|
||||
@ -1,80 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html;charset=utf-8">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no">
|
||||
<link rel="icon" type="image/png" href="assets/images/3dviewer_net_favicon.png">
|
||||
|
||||
<title>Online 3D Viewer</title>
|
||||
|
||||
<!-- libs start -->
|
||||
<script type="text/javascript" src="../libs/jquery-3.5.1.min.js"></script>
|
||||
<script type="text/javascript" src="../libs/three.min-126.js"></script>
|
||||
<!-- libs end -->
|
||||
|
||||
<!-- importer start -->
|
||||
<script type="text/javascript" src="../source/core/core.js"></script>
|
||||
<script type="text/javascript" src="../source/core/taskrunner.js"></script>
|
||||
<script type="text/javascript" src="../source/geometry/geometry.js"></script>
|
||||
<script type="text/javascript" src="../source/geometry/coord2d.js"></script>
|
||||
<script type="text/javascript" src="../source/geometry/coord3d.js"></script>
|
||||
<script type="text/javascript" src="../source/geometry/matrix.js"></script>
|
||||
<script type="text/javascript" src="../source/geometry/transformation.js"></script>
|
||||
<script type="text/javascript" src="../source/geometry/tween.js"></script>
|
||||
<script type="text/javascript" src="../source/io/binaryreader.js"></script>
|
||||
<script type="text/javascript" src="../source/io/binarywriter.js"></script>
|
||||
<script type="text/javascript" src="../source/io/textwriter.js"></script>
|
||||
<script type="text/javascript" src="../source/io/bufferutils.js"></script>
|
||||
<script type="text/javascript" src="../source/io/fileutils.js"></script>
|
||||
<script type="text/javascript" src="../source/model/modelentities.js"></script>
|
||||
<script type="text/javascript" src="../source/model/mesh.js"></script>
|
||||
<script type="text/javascript" src="../source/model/meshbuffer.js"></script>
|
||||
<script type="text/javascript" src="../source/model/model.js"></script>
|
||||
<script type="text/javascript" src="../source/model/modelutils.js"></script>
|
||||
<script type="text/javascript" src="../source/model/modelfinalization.js"></script>
|
||||
<script type="text/javascript" src="../source/import/importerutils.js"></script>
|
||||
<script type="text/javascript" src="../source/import/importerbase.js"></script>
|
||||
<script type="text/javascript" src="../source/import/importerobj.js"></script>
|
||||
<script type="text/javascript" src="../source/import/importerstl.js"></script>
|
||||
<script type="text/javascript" src="../source/import/importeroff.js"></script>
|
||||
<script type="text/javascript" src="../source/import/importerply.js"></script>
|
||||
<script type="text/javascript" src="../source/import/importer3ds.js"></script>
|
||||
<script type="text/javascript" src="../source/import/importergltf.js"></script>
|
||||
<script type="text/javascript" src="../source/import/importer.js"></script>
|
||||
<script type="text/javascript" src="../source/export/exporterutils.js"></script>
|
||||
<script type="text/javascript" src="../source/export/exporterbase.js"></script>
|
||||
<script type="text/javascript" src="../source/export/exporterobj.js"></script>
|
||||
<script type="text/javascript" src="../source/export/exporterstl.js"></script>
|
||||
<script type="text/javascript" src="../source/export/exporterply.js"></script>
|
||||
<script type="text/javascript" src="../source/export/exporteroff.js"></script>
|
||||
<script type="text/javascript" src="../source/export/exportergltf.js"></script>
|
||||
<script type="text/javascript" src="../source/export/exporter.js"></script>
|
||||
<script type="text/javascript" src="../source/external/three.converter.js"></script>
|
||||
<script type="text/javascript" src="../source/external/three.model.loader.js"></script>
|
||||
<script type="text/javascript" src="../source/viewer/domutils.js"></script>
|
||||
<script type="text/javascript" src="../source/viewer/navigation.js"></script>
|
||||
<script type="text/javascript" src="../source/viewer/viewer.js"></script>
|
||||
<script type="text/javascript" src="../source/viewer/domviewer.js"></script>
|
||||
<!-- importer end -->
|
||||
|
||||
<script type='text/javascript'>
|
||||
OV.Init3DViewerElements ();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
div.online_3d_viewer
|
||||
{
|
||||
float: left;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="online_3d_viewer" width="400" height="300" model="../test/testfiles/3ds/cube_four_instances.3ds,../test/testfiles/3ds/texture.png"></div>
|
||||
<div class="online_3d_viewer" width="400" height="300" model="../test/testfiles/3ds/cube_four_instances.3ds,../test/testfiles/3ds/texture.png" camera="3.929421317669367,6.153966358575169,2.7076091223424714,1.5,1.5,0.5,0,0,1"></div>
|
||||
<div class="online_3d_viewer" width="400" height="300" model="../test/testfiles/obj/hundred_cubes.obj,../test/testfiles/obj/hundred_cubes.mtl"></div>
|
||||
<div class="online_3d_viewer" width="400" height="300" model="wrong.3ds"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@ -47,8 +47,8 @@
|
||||
|
||||
<h2 id="supported_file_formats">Supported file formats</h2>
|
||||
<p>
|
||||
The website supports several file formats for import and export. If a file format has an ascii and binary version,
|
||||
usually it's recommended to use the binary version. During export an ascii file is created.
|
||||
The website supports several file formats for import and export. If a file format has text and binary version,
|
||||
usually it's recommended to use the binary version.
|
||||
</p>
|
||||
<p>
|
||||
<table>
|
||||
@ -60,7 +60,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>obj</td>
|
||||
<td>ascii</td>
|
||||
<td>text</td>
|
||||
<td class="center green">✓</td>
|
||||
<td class="center green">✓</td>
|
||||
<td>with mtl and texture support</td>
|
||||
@ -74,7 +74,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2">stl</td>
|
||||
<td>ascii</td>
|
||||
<td>text</td>
|
||||
<td class="center green">✓</td>
|
||||
<td class="center green">✓</td>
|
||||
<td>binary version is preferred</td>
|
||||
@ -87,7 +87,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2">ply</td>
|
||||
<td>ascii</td>
|
||||
<td>text</td>
|
||||
<td class="center green">✓</td>
|
||||
<td class="center green">✓</td>
|
||||
<td>binary version is preferred</td>
|
||||
@ -100,20 +100,20 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2">gltf</td>
|
||||
<td>ascii (.gltf)</td>
|
||||
<td>text (.gltf)</td>
|
||||
<td class="center green">✓</td>
|
||||
<td class="center green">✓</td>
|
||||
<td class="center red">✗</td>
|
||||
<td>version 2.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>binary (.glb)</td>
|
||||
<td class="center green">✓</td>
|
||||
<td class="center red">✗</td>
|
||||
<td class="center green">✓</td>
|
||||
<td>version 2.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2">off</td>
|
||||
<td>ascii</td>
|
||||
<td>text</td>
|
||||
<td class="center green">✓</td>
|
||||
<td class="center green">✓</td>
|
||||
<td>-</td>
|
||||
|
||||
@ -118,13 +118,13 @@ table td.center
|
||||
|
||||
table td.green
|
||||
{
|
||||
color: #21C23C;
|
||||
color: #21c23c;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table td.red
|
||||
{
|
||||
color: #BF233D;
|
||||
color: #bf233d;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@ -141,6 +141,8 @@ div.code
|
||||
font-size: 14px;
|
||||
margin: 10px 0px;
|
||||
padding : 5px;
|
||||
overflow: auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1000px)
|
||||
|
||||
@ -54,7 +54,7 @@ OV.ButtonDialog = class
|
||||
let contentDiv = this.modal.GetContentDiv ();
|
||||
contentDiv.addClass ('ov_dialog');
|
||||
|
||||
let titleDiv = $('<div>').addClass ('ov_dialog_title').html (title).appendTo (contentDiv);
|
||||
$('<div>').addClass ('ov_dialog_title').html (title).appendTo (contentDiv);
|
||||
let dialogContentDiv = $('<div>').addClass ('ov_dialog_content').appendTo (contentDiv);
|
||||
let buttonsDiv = $('<div>').addClass ('ov_dialog_buttons').appendTo (contentDiv);
|
||||
let buttonsInnerDiv = $('<div>').addClass ('ov_dialog_buttons_inner').appendTo (buttonsDiv);
|
||||
@ -65,6 +65,11 @@ OV.ButtonDialog = class
|
||||
return dialogContentDiv;
|
||||
}
|
||||
|
||||
SetCloseHandler (closeHandler)
|
||||
{
|
||||
this.modal.SetCloseHandler (closeHandler);
|
||||
}
|
||||
|
||||
Show ()
|
||||
{
|
||||
this.modal.Open ();
|
||||
@ -152,14 +157,14 @@ OV.ShowOpenUrlDialog = function (onOk)
|
||||
{
|
||||
for (let i = 0; i < urls.length; i++) {
|
||||
let url = urls[i];
|
||||
if (url.search ('www.dropbox.com') !== -1) {
|
||||
if (url.search (/www\.dropbox\.com/u) !== -1) {
|
||||
url = url.replace ('www.dropbox.com', 'dl.dropbox.com');
|
||||
let separatorPos = url.indexOf ('?');
|
||||
if (separatorPos !== -1) {
|
||||
url = url.substr (0, separatorPos);
|
||||
}
|
||||
urls[i] = url;
|
||||
} else if (url.search ('github.com') !== -1) {
|
||||
} else if (url.search (/github\.com/u) !== -1) {
|
||||
url = url.replace ('github.com', 'raw.githubusercontent.com');
|
||||
url = url.replace ('/blob', '');
|
||||
let separatorPos = url.indexOf ('?');
|
||||
@ -201,7 +206,7 @@ OV.ShowOpenUrlDialog = function (onOk)
|
||||
return dialog;
|
||||
};
|
||||
|
||||
OV.ShowExportDialog = function (model, importer)
|
||||
OV.ShowExportDialog = function (model)
|
||||
{
|
||||
if (model === null) {
|
||||
return OV.ShowMessageDialog ('Export Failed', 'Please load a model to export', null);
|
||||
@ -221,12 +226,14 @@ OV.ShowExportDialog = function (model, importer)
|
||||
$('<div>').html (text).addClass ('ov_dialog_section').appendTo (contentDiv);
|
||||
|
||||
let formats = [
|
||||
{ name : 'obj (ascii)', format : OV.FileFormat.Text, extension : 'obj' },
|
||||
{ name : 'stl (ascii)', format : OV.FileFormat.Text, extension : 'stl' },
|
||||
{ name : 'obj (text)', format : OV.FileFormat.Text, extension : 'obj' },
|
||||
{ name : 'stl (text)', format : OV.FileFormat.Text, extension : 'stl' },
|
||||
{ name : 'stl (binary)', format : OV.FileFormat.Binary, extension : 'stl' },
|
||||
{ name : 'ply (ascii)', format : OV.FileFormat.Text, extension : 'ply' },
|
||||
{ name : 'ply (text)', format : OV.FileFormat.Text, extension : 'ply' },
|
||||
{ name : 'ply (binary)', format : OV.FileFormat.Binary, extension : 'ply' },
|
||||
{ name : 'off (ascii)', format : OV.FileFormat.Text, extension : 'off' }
|
||||
{ name : 'gltf (text)', format : OV.FileFormat.Text, extension : 'gltf' },
|
||||
{ name : 'gltf (binary)', format : OV.FileFormat.Binary, extension : 'glb' },
|
||||
{ name : 'off (text)', format : OV.FileFormat.Text, extension : 'off' }
|
||||
];
|
||||
|
||||
let formatSelect = $('<select>').addClass ('ov_dialog_select').appendTo (contentDiv);
|
||||
@ -239,6 +246,7 @@ OV.ShowExportDialog = function (model, importer)
|
||||
let fileListSection = $('<div>').addClass ('ov_dialog_section').appendTo (contentDiv);
|
||||
let fileList = $('<div>').addClass ('ov_dialog_file_list').addClass ('ov_thin_scrollbar').appendTo (fileListSection);
|
||||
|
||||
let createdUrls = [];
|
||||
formatSelect.change (function () {
|
||||
fileList.empty ();
|
||||
let selectedIndex = formatSelect.prop ('selectedIndex');
|
||||
@ -252,14 +260,14 @@ OV.ShowExportDialog = function (model, importer)
|
||||
taskRunner.Run (1, {
|
||||
runTask : function (index, ready) {
|
||||
let exporter = new OV.Exporter ();
|
||||
let files = exporter.Export (model, selectedFormat.format, selectedFormat.extension, null);
|
||||
let files = exporter.Export (model, selectedFormat.format, selectedFormat.extension);
|
||||
fileList.empty ();
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
let file = files[i];
|
||||
let url = file.GetUrl ();
|
||||
if (url === null) {
|
||||
let fileBlob = new Blob ([file.GetContent ()]);
|
||||
url = URL.createObjectURL (fileBlob);
|
||||
url = OV.CreateObjectUrl (file.GetContent ());
|
||||
createdUrls.push (url);
|
||||
}
|
||||
let fileLink = $('<a>').addClass ('ov_dialog_file_link').appendTo (fileList);
|
||||
fileLink.attr ('href', url);
|
||||
@ -272,6 +280,11 @@ OV.ShowExportDialog = function (model, importer)
|
||||
});
|
||||
});
|
||||
|
||||
dialog.SetCloseHandler (function () {
|
||||
for (let i = 0; i < createdUrls.length; i++) {
|
||||
OV.RevokeObjectUrl (createdUrls[i]);
|
||||
}
|
||||
});
|
||||
dialog.Show ();
|
||||
return dialog;
|
||||
};
|
||||
|
||||
@ -1,24 +1,24 @@
|
||||
OV.InitModelLoader = function (modelLoader, callbacks)
|
||||
{
|
||||
function OpenErrorDialog (importerError)
|
||||
function OpenErrorDialog (importError)
|
||||
{
|
||||
if (importerError.code === OV.ImporterErrorCode.NoImportableFile) {
|
||||
if (importError.code === OV.ImportErrorCode.NoImportableFile) {
|
||||
return OV.ShowMessageDialog (
|
||||
'Something went wrong',
|
||||
'No importable file found. You can open obj, 3ds, stl, ply, gltf, glb and off files.',
|
||||
importerError.message
|
||||
importError.message
|
||||
);
|
||||
} else if (importerError.code === OV.ImporterErrorCode.ImportFailed) {
|
||||
} else if (importError.code === OV.ImportErrorCode.ImportFailed) {
|
||||
return OV.ShowMessageDialog (
|
||||
'Something went wrong',
|
||||
'Failed to import model.',
|
||||
importerError.message
|
||||
importError.message
|
||||
);
|
||||
} else {
|
||||
return OV.ShowMessageDialog (
|
||||
'Something went wrong',
|
||||
'Unknown error.',
|
||||
importerError.message
|
||||
importError.message
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -53,9 +53,9 @@ OV.InitModelLoader = function (modelLoader, callbacks)
|
||||
onTextureLoaded : function () {
|
||||
callbacks.onRender ();
|
||||
},
|
||||
onLoadError : function (importerError) {
|
||||
onLoadError : function (importError) {
|
||||
progressDialog.Hide ();
|
||||
errorDialog = OpenErrorDialog (importerError);
|
||||
errorDialog = OpenErrorDialog (importError);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@ -6,6 +6,7 @@ OV.Modal = class
|
||||
this.overlayDiv = null;
|
||||
this.resizeHandler = null;
|
||||
this.customResizeHandler = null;
|
||||
this.closeHandler = null;
|
||||
this.isOpen = false;
|
||||
this.closeable = true;
|
||||
}
|
||||
@ -25,6 +26,11 @@ OV.Modal = class
|
||||
this.customResizeHandler = customResizeHandler;
|
||||
}
|
||||
|
||||
SetCloseHandler (closeHandler)
|
||||
{
|
||||
this.closeHandler = closeHandler;
|
||||
}
|
||||
|
||||
Open ()
|
||||
{
|
||||
let windowObj = $(window);
|
||||
@ -54,6 +60,9 @@ OV.Modal = class
|
||||
|
||||
let windowObj = $(window);
|
||||
windowObj.unbind ('resize', this.resizeHandler);
|
||||
if (this.closeHandler !== null) {
|
||||
this.closeHandler ();
|
||||
}
|
||||
|
||||
this.modalDiv.remove ();
|
||||
this.overlayDiv.remove ();
|
||||
|
||||
@ -27,7 +27,7 @@ OV.IsHoverEnabled = function ()
|
||||
|
||||
OV.IsSmallWidth = function ()
|
||||
{
|
||||
if (window.matchMedia ('(max-width : 600px)').matches) {
|
||||
if (window.matchMedia ('(max-width : 700px)').matches) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@ -36,7 +36,7 @@ OV.IsSmallWidth = function ()
|
||||
|
||||
OV.IsSmallHeight = function ()
|
||||
{
|
||||
if (window.matchMedia ('(max-height : 600px)').matches) {
|
||||
if (window.matchMedia ('(max-height : 700px)').matches) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@ -103,8 +103,9 @@ OV.CopyToClipboard = function (text)
|
||||
OV.CreateIconButton = function (iconName, hoverIconName, title, link)
|
||||
{
|
||||
let buttonLink = $('<a>');
|
||||
buttonLink.attr ('target', '_blank');
|
||||
buttonLink.attr ('href', link);
|
||||
buttonLink.attr ('target', '_blank');
|
||||
buttonLink.attr ('rel', 'noopener noreferrer');
|
||||
OV.InstallTooltip (buttonLink, title);
|
||||
let imgElem = $('<img>').attr ('src', iconName).appendTo (buttonLink);
|
||||
if (hoverIconName !== null && OV.IsHoverEnabled ()) {
|
||||
|
||||
@ -627,7 +627,7 @@ div.ov_menu_info_panel div.ov_info_box_button:hover
|
||||
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 600px)
|
||||
@media only screen and (max-width: 700px)
|
||||
{
|
||||
|
||||
.only_full_width
|
||||
|
||||
@ -71,6 +71,9 @@ OV.Website = class
|
||||
this.dialog.Hide ();
|
||||
this.dialog = null;
|
||||
}
|
||||
if (this.model !== null) {
|
||||
OV.RevokeModelUrls (this.model);
|
||||
}
|
||||
this.model = null;
|
||||
this.parameters.introDiv.hide ();
|
||||
this.ShowViewer (false);
|
||||
@ -265,7 +268,7 @@ OV.Website = class
|
||||
});
|
||||
AddSeparator (this.toolbar, true);
|
||||
AddButton (this.toolbar, 'export', 'Export model', true, function () {
|
||||
obj.dialog = OV.ShowExportDialog (obj.model, obj.modelLoader.GetImporter ());
|
||||
obj.dialog = OV.ShowExportDialog (obj.model);
|
||||
});
|
||||
AddButton (this.toolbar, 'embed', 'Get embedding code', true, function () {
|
||||
obj.dialog = OV.ShowEmbeddingDialog (obj.modelLoader.GetImporter (), obj.viewer.GetCamera ());
|
||||
|
||||
Loading…
Reference in New Issue
Block a user