Merge branch 'dev'

This commit is contained in:
Viktor Kovacs 2021-04-11 11:00:43 +02:00
commit 040faf8ccc
31 changed files with 787 additions and 394 deletions

12
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,12 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: ['https://www.paypal.com/donate?hosted_button_id=N8V9R8JFH2SE2']

View File

@ -1,7 +1,7 @@
{
"name": "online-3d-viewer",
"description": "Online 3D Viewer",
"version": "0.7.6",
"version": "0.7.7",
"repository": "github:kovacsv/Online3DViewer",
"license": "MIT",
"devDependencies": {

View File

@ -3,7 +3,6 @@ OV.ExportedFile = class
constructor (name)
{
this.name = name;
this.url = null;
this.content = null;
}
@ -17,16 +16,6 @@ OV.ExportedFile = class
this.name = name;
}
GetUrl ()
{
return this.url;
}
SetUrl (url)
{
this.url = url;
}
GetContent ()
{
return this.content;

View File

@ -50,7 +50,7 @@ OV.ExporterGltf = class extends OV.ExporterBase
let textureIndex = fileNameToIndex[fileName];
if (textureIndex === undefined) {
let textureFile = new OV.ExportedFile (fileName);
textureFile.SetUrl (texture.url);
textureFile.SetContent (texture.buffer);
files.push (textureFile);
textureIndex = mainJson.textures.length;
@ -244,19 +244,10 @@ OV.ExporterGltf = class extends OV.ExporterBase
}
}
function AddBufferView (mainJson, offset, length)
{
mainJson.bufferViews.push ({
buffer : 0,
byteOffset : offset,
byteLength : length,
});
return offset + length;
}
let mainJson = {
asset : {
version : '2.0'
version : '2.0',
generator : 'https://3dviewer.net'
},
scene : 0,
scenes : [

View File

@ -25,7 +25,7 @@ OV.ExporterObj = class extends OV.ExporterBase
});
if (fileIndex === -1) {
let textureFile = new OV.ExportedFile (fileName);
textureFile.SetUrl (texture.url);
textureFile.SetContent (texture.buffer);
files.push (textureFile);
}
}
@ -37,6 +37,7 @@ OV.ExporterObj = class extends OV.ExporterBase
files.push (objFile);
let mtlWriter = new OV.TextWriter ();
mtlWriter.WriteLine (this.GetHeaderText ());
for (let materialIndex = 0; materialIndex < model.MaterialCount (); materialIndex++) {
let material = model.GetMaterial (materialIndex);
mtlWriter.WriteArrayLine (['newmtl', this.GetExportedMaterialName (material.name)]);
@ -52,6 +53,7 @@ OV.ExporterObj = class extends OV.ExporterBase
mtlFile.SetContent (mtlWriter.GetText ());
let objWriter = new OV.TextWriter ();
objWriter.WriteLine (this.GetHeaderText ());
objWriter.WriteArrayLine (['mtllib', mtlFile.GetName ()]);
let vertexOffset = 0;
let normalOffset = 0;
@ -105,4 +107,9 @@ OV.ExporterObj = class extends OV.ExporterBase
objFile.SetContent (objWriter.GetText ());
}
GetHeaderText ()
{
return '# exported by https://3dviewer.net';
}
};

View File

@ -12,12 +12,7 @@ OV.ThreeModelLoader = class
this.callbacks = callbacks;
}
SetDefaultColor (defaultColor)
{
this.importer.SetDefaultColor (defaultColor);
}
LoadFromUrlList (urls)
LoadFromUrlList (urls, settings)
{
if (this.inProgress) {
return;
@ -27,11 +22,11 @@ OV.ThreeModelLoader = class
this.inProgress = true;
this.callbacks.onLoadStart ();
this.importer.LoadFilesFromUrls (urls, function () {
obj.OnFilesLoaded ();
obj.OnFilesLoaded (settings);
});
}
LoadFromFileList (files)
LoadFromFileList (files, settings)
{
if (this.inProgress) {
return;
@ -41,11 +36,11 @@ OV.ThreeModelLoader = class
this.inProgress = true;
this.callbacks.onLoadStart ();
this.importer.LoadFilesFromFileObjects (files, function () {
obj.OnFilesLoaded ();
obj.OnFilesLoaded (settings);
});
}
ReloadFiles ()
ReloadFiles (settings)
{
if (this.inProgress) {
return;
@ -53,15 +48,15 @@ OV.ThreeModelLoader = class
this.inProgress = true;
this.callbacks.onLoadStart ();
this.OnFilesLoaded ();
this.OnFilesLoaded (settings);
}
OnFilesLoaded ()
OnFilesLoaded (settings)
{
let obj = this;
this.callbacks.onImportStart ();
OV.RunTaskAsync (function () {
obj.importer.Import ({
obj.importer.Import (settings, {
success : function (importResult) {
obj.OnModelImported (importResult);
},

View File

@ -179,6 +179,14 @@ OV.FileList = class
}
};
OV.ImportSettings = class
{
constructor ()
{
this.defaultColor = new OV.Color (200, 200, 200);
}
};
OV.ImportErrorCode =
{
NoImportableFile : 1,
@ -207,7 +215,7 @@ OV.ImportResult = class
}
};
OV.ImporterBuffers = class
OV.ImportBuffers = class
{
constructor (getBufferCallback)
{
@ -260,7 +268,6 @@ OV.Importer = class
new OV.ImporterGltf ()
];
this.fileList = new OV.FileList (this.importers);
this.defaultColor = new OV.Color (200, 200, 200);
this.model = null;
this.usedFiles = [];
this.missingFiles = [];
@ -276,12 +283,7 @@ OV.Importer = class
this.LoadFiles (fileList, OV.FileSource.File, onReady);
}
SetDefaultColor (defaultColor)
{
this.defaultColor = defaultColor;
}
Import (callbacks)
Import (settings, callbacks)
{
let mainFile = this.fileList.GetMainFile ();
if (mainFile === null || mainFile.file === null || mainFile.file.content === null) {
@ -300,7 +302,7 @@ OV.Importer = class
let obj = this;
let importer = mainFile.importer;
let buffers = new OV.ImporterBuffers (function (fileName) {
let buffers = new OV.ImportBuffers (function (fileName) {
let fileBuffer = null;
let file = obj.fileList.FindFileByPath (fileName);
if (file === null || file.content === null) {
@ -316,7 +318,7 @@ OV.Importer = class
importer.Import (mainFile.file.content, mainFile.file.extension, {
getDefaultMaterial : function () {
let material = new OV.Material ();
material.diffuse = obj.defaultColor;
material.diffuse = settings.defaultColor;
return material;
},
getFileBuffer : function (filePath) {

View File

@ -25,23 +25,6 @@ OV.Color = class
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);
@ -253,3 +236,37 @@ OV.Triangle = class
return cloned;
}
};
OV.ColorToHexString = function (color)
{
function IntegerToHex (intVal)
{
let result = parseInt (intVal, 10).toString (16);
while (result.length < 2) {
result = '0' + result;
}
return result;
}
let r = IntegerToHex (color.r);
let g = IntegerToHex (color.g);
let b = IntegerToHex (color.b);
return r + g + b;
};
OV.HexStringToColor = function (hexString)
{
if (hexString.length !== 6) {
return null;
}
let r = parseInt (hexString.substr (0, 2), 16);
let g = parseInt (hexString.substr (2, 2), 16);
let b = parseInt (hexString.substr (4, 2), 16);
return new OV.Color (r, g, b);
};
OV.ColorIsEqual = function (a, b)
{
return a.r === b.r && a.g === b.g && a.b === b.b;
};

View File

@ -1,5 +1,15 @@
OV.ParameterConverter =
{
IntegerToString (integer)
{
return integer.toString ();
},
StringToInteger (str)
{
return parseInt (str, 10);
},
NumberToString (number)
{
let precision = 5;
@ -11,6 +21,22 @@ OV.ParameterConverter =
return parseFloat (str);
},
ModelUrlsToString : function (urls)
{
if (urls === null) {
return null;
}
return urls.join (',');
},
StringToModelUrls : function (str)
{
if (str === null || str.length === 0) {
return null;
}
return str.split (',');
},
CameraToString : function (camera)
{
if (camera === null) {
@ -24,12 +50,12 @@ OV.ParameterConverter =
return cameraParameters;
},
StringToCamera : function (urlParam)
StringToCamera : function (str)
{
if (urlParam === null || urlParam.length === 0) {
if (str === null || str.length === 0) {
return null;
}
let paramParts = urlParam.split (',');
let paramParts = str.split (',');
if (paramParts.length !== 9) {
return null;
}
@ -41,20 +67,34 @@ OV.ParameterConverter =
return camera;
},
ModelUrlsToString : function (urls)
ColorToString : function (color)
{
if (urls === null) {
if (color === null) {
return null;
}
return urls.join (',');
let colorParameters = [
this.IntegerToString (color.r),
this.IntegerToString (color.g),
this.IntegerToString (color.b)
].join (',');
return colorParameters;
},
StringToModelUrls : function (urlParam)
StringToColor : function (str)
{
if (urlParam === null || urlParam.length === 0) {
if (str === null || str.length === 0) {
return null;
}
return urlParam.split (',');
let paramParts = str.split (',');
if (paramParts.length !== 3) {
return null;
}
let color = new OV.Color (
this.StringToInteger (paramParts[0]),
this.StringToInteger (paramParts[1]),
this.StringToInteger (paramParts[2])
);
return color;
}
};
@ -63,7 +103,7 @@ OV.ParameterListBuilder = class
constructor (separator)
{
this.separator = separator;
this.urlParams = '';
this.paramList = '';
}
AddModelUrls (urls)
@ -78,36 +118,42 @@ OV.ParameterListBuilder = class
return this;
}
AddColor (color)
{
this.AddUrlPart ('color', OV.ParameterConverter.ColorToString (color));
return this;
}
AddUrlPart (keyword, urlPart)
{
if (keyword === null || urlPart === null) {
return;
}
if (this.urlParams.length > 0) {
this.urlParams += this.separator;
if (this.paramList.length > 0) {
this.paramList += this.separator;
}
this.urlParams += keyword + '=' + urlPart;
this.paramList += keyword + '=' + urlPart;
}
GetUrlParams ()
GetParameterList ()
{
return this.urlParams;
return this.paramList;
}
};
OV.ParameterListParser = class
{
constructor (urlParams, separator)
constructor (paramList, separator)
{
this.separator = separator;
this.urlParams = urlParams;
this.paramList = paramList;
}
GetModelUrls ()
{
// detect legacy links
if (this.urlParams.indexOf ('=') === -1) {
return this.urlParams.split (',');
if (this.paramList.indexOf ('=') === -1) {
return this.paramList.split (',');
}
let keywordParams = this.GetKeywordParams ('model');
@ -119,14 +165,20 @@ OV.ParameterListParser = class
let keywordParams = this.GetKeywordParams ('camera');
return OV.ParameterConverter.StringToCamera (keywordParams);
}
GetColor ()
{
let colorParams = this.GetKeywordParams ('color');
return OV.ParameterConverter.StringToColor (colorParams);
}
GetKeywordParams (keyword)
{
if (this.urlParams === null || this.urlParams.length === 0) {
if (this.paramList === null || this.paramList.length === 0) {
return null;
}
let keywordToken = keyword + '=';
let urlParts = this.urlParams.split (this.separator);
let urlParts = this.paramList.split (this.separator);
for (let i = 0; i < urlParts.length; i++) {
let urlPart = urlParts[i];
if (urlPart.startsWith (keywordToken)) {
@ -147,10 +199,9 @@ OV.CreateUrlParser = function (urlParams)
return new OV.ParameterListParser (urlParams, '$');
};
OV.CreateUrlParameters = function (urls, camera)
OV.CreateModelUrlParameters = function (urls)
{
let builder = OV.CreateUrlBuilder ();
builder.AddModelUrls (urls);
builder.AddCamera (camera);
return builder.GetUrlParams ();
return builder.GetParameterList ();
};

View File

@ -1,19 +1,5 @@
OV.Init3DViewerElements = function ()
{
function SetCamera (element, viewer, importResult)
{
let camera = null;
let cameraParams = element.getAttribute ('camera');
if (cameraParams) {
camera = OV.ParameterConverter.StringToCamera (cameraParams);
}
if (camera !== null) {
viewer.SetCamera (camera);
} else {
viewer.SetUpVector (importResult.upVector, false);
}
}
function LoadElement (element)
{
let canvas = document.createElement ('canvas');
@ -50,7 +36,16 @@ OV.Init3DViewerElements = function ()
return true;
});
viewer.AdjustClippingPlanes (boundingSphere);
SetCamera (element, viewer, importResult);
let camera = null;
let cameraParams = element.getAttribute ('camera');
if (cameraParams) {
camera = OV.ParameterConverter.StringToCamera (cameraParams);
}
if (camera !== null) {
viewer.SetCamera (camera);
} else {
viewer.SetUpVector (importResult.upVector, false);
}
viewer.FitToWindow (boundingSphere, false);
},
onTextureLoaded : function () {
@ -71,7 +66,16 @@ OV.Init3DViewerElements = function ()
return;
}
loader.LoadFromUrlList (modelUrls);
let settings = new OV.ImportSettings ();
let colorParams = element.getAttribute ('color');
if (colorParams) {
let color = OV.ParameterConverter.StringToColor (colorParams);
if (color !== null) {
settings.defaultColor = color;
}
}
loader.LoadFromUrlList (modelUrls, settings);
return {
element: element,
viewer: viewer

View File

@ -387,7 +387,7 @@ OV.Viewer = class
this.ResizeRenderer (width, height);
}
this.Render ();
let url = this.renderer.domElement.toDataURL();
let url = this.renderer.domElement.toDataURL ();
if (originalSize !== null) {
this.ResizeRenderer (
parseInt (originalSize.x, 10),

View File

@ -12,15 +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.diffuseMap.buffer = new ArrayBuffer (1);
material1.specularMap = new OV.TextureMap ();
material1.specularMap.name = 'textures/texture2.png';
material1.specularMap.url = 'texture2_url';
material1.specularMap.buffer = new ArrayBuffer (4);
material1.specularMap.buffer = new ArrayBuffer (2);
material1.bumpMap = new OV.TextureMap ();
material1.bumpMap.name = 'textures/texture3.png';
material1.bumpMap.url = 'texture3_url';
material1.bumpMap.buffer = new ArrayBuffer (4);
material1.bumpMap.buffer = new ArrayBuffer (3);
model.AddMaterial (material1);
let material2 = new OV.Material ();
@ -74,6 +74,7 @@ describe ('Exporter', function () {
assert.strictEqual (mtlFile.GetName (), 'model.mtl');
assert.strictEqual (mtlFile.GetContent (),
[
'# exported by https://3dviewer.net',
'newmtl TestMaterial1',
'Ka 0 0 0',
'Kd 1 0 0',
@ -95,6 +96,7 @@ describe ('Exporter', function () {
assert.strictEqual (objFile.GetName (), 'model.obj');
assert.strictEqual (objFile.GetContent (),
[
'# exported by https://3dviewer.net',
'mtllib model.mtl',
'g TestMesh1',
'v 0 0 1',
@ -121,18 +123,15 @@ describe ('Exporter', function () {
let textureFile1 = result[2];
assert.strictEqual (textureFile1.GetName (), 'texture1.png');
assert.strictEqual (textureFile1.GetUrl (), 'texture1_url');
assert.strictEqual (textureFile1.GetContent (), null);
assert.strictEqual (textureFile1.GetContent ().byteLength, 1);
let textureFile2 = result[3];
assert.strictEqual (textureFile2.GetName (), 'texture2.png');
assert.strictEqual (textureFile2.GetUrl (), 'texture2_url');
assert.strictEqual (textureFile2.GetContent (), null);
assert.strictEqual (textureFile2.GetContent ().byteLength, 2);
let textureFile3 = result[4];
assert.strictEqual (textureFile3.GetName (), 'texture3.png');
assert.strictEqual (textureFile3.GetUrl (), 'texture3_url');
assert.strictEqual (textureFile3.GetContent (), null);
assert.strictEqual (textureFile3.GetContent ().byteLength, 3);
});
it ('Stl Export', function () {
@ -305,8 +304,7 @@ describe ('Exporter', function () {
assert.strictEqual (binFile.GetName (), 'model.bin');
assert.strictEqual (textureFile.GetName (), 'texture1.png');
assert.strictEqual (textureFile.GetUrl (), 'texture1_url');
assert.strictEqual (textureFile.GetContent (), null);
assert.strictEqual (textureFile.GetContent ().byteLength, 1);
let contentBuffer = gltfFile.GetContent ();
let importer = new OV.ImporterGltf ();

View File

@ -4,7 +4,8 @@ var path = require ('path');
function ImportFilesWithImporter (importer, files, callbacks)
{
importer.LoadFilesFromFileObjects (files, function () {
importer.Import ({
let settings = new OV.ImportSettings ();
importer.Import (settings, {
success : function (importResult) {
callbacks.success (importer, importResult);
},
@ -239,18 +240,21 @@ describe ('Importer Test', function () {
new FileObject ('stl', 'single_triangle.stl')
];
let theImporter = new OV.Importer ();
theImporter.SetDefaultColor (new OV.Color (200, 0, 0));
ImportFilesWithImporter (theImporter, files, {
success : function (importer, importResult) {
assert (!OV.IsModelEmpty (importResult.model));
assert.deepStrictEqual (importResult.usedFiles, ['single_triangle.stl']);
assert.deepStrictEqual (importResult.missingFiles, []);
let material = importResult.model.GetMaterial (0);
assert.deepStrictEqual (material.diffuse, new OV.Color (200, 0, 0));
},
error : function (importer, importError) {
assert.fail ();
}
theImporter.LoadFilesFromFileObjects (files, function () {
let settings = new OV.ImportSettings ();
settings.defaultColor = new OV.Color (200, 0, 0);
theImporter.Import (settings, {
success : function (importResult) {
assert (!OV.IsModelEmpty (importResult.model));
assert.deepStrictEqual (importResult.usedFiles, ['single_triangle.stl']);
assert.deepStrictEqual (importResult.missingFiles, []);
let material = importResult.model.GetMaterial (0);
assert.deepStrictEqual (material.diffuse, new OV.Color (200, 0, 0));
},
error : function (importError) {
assert.fail ();
}
});
});
});
});

View File

@ -150,3 +150,19 @@ describe ('Model Finalization', function() {
assert.strictEqual (normal.z, 0.7071067811865475);
});
});
describe ('Color Conversion', function() {
it ('Color equality check', function () {
assert (OV.ColorIsEqual (new OV.Color (10, 20, 30), new OV.Color (10, 20, 30)));
assert (!OV.ColorIsEqual (new OV.Color (10, 20, 30), new OV.Color (11, 20, 30)));
assert (!OV.ColorIsEqual (new OV.Color (10, 20, 30), new OV.Color (10, 21, 30)));
assert (!OV.ColorIsEqual (new OV.Color (10, 20, 30), new OV.Color (10, 20, 31)));
});
it ('Color hex string conversion', function () {
let color = new OV.Color (10, 20, 30);
let hexString = '0a141e';
assert.strictEqual (OV.ColorToHexString (color), hexString);
assert.deepStrictEqual (OV.HexStringToColor (hexString), color);
});
});

View File

@ -8,12 +8,15 @@ describe ('Parameter List', function () {
new OV.Coord3D (0.0, 0.0, 0.0),
new OV.Coord3D (0.0, 0.0, 1.0)
);
let urlParams1 = OV.CreateUrlBuilder ().AddModelUrls (modelUrls).GetUrlParams ();
let urlParams2 = OV.CreateUrlBuilder ().AddCamera (camera).GetUrlParams ();
let urlParams3 = OV.CreateUrlBuilder ().AddModelUrls (modelUrls).AddCamera (camera).GetUrlParams ();
let color = new OV.Color (1, 2, 3);
let urlParams1 = OV.CreateUrlBuilder ().AddModelUrls (modelUrls).GetParameterList ();
let urlParams2 = OV.CreateUrlBuilder ().AddCamera (camera).GetParameterList ();
let urlParams3 = OV.CreateUrlBuilder ().AddModelUrls (modelUrls).AddCamera (camera).GetParameterList ();
let urlParams4 = OV.CreateUrlBuilder ().AddModelUrls (modelUrls).AddCamera (camera).AddColor (color).GetParameterList ();
assert.strictEqual (urlParams1, 'model=a.txt,b.txt');
assert.strictEqual (urlParams2, 'camera=1.0000,1.0000,1.0000,0.0000,0.0000,0.0000,0.0000,0.0000,1.0000');
assert.strictEqual (urlParams3, 'model=a.txt,b.txt$camera=1.0000,1.0000,1.0000,0.0000,0.0000,0.0000,0.0000,0.0000,1.0000');
assert.strictEqual (urlParams4, 'model=a.txt,b.txt$camera=1.0000,1.0000,1.0000,0.0000,0.0000,0.0000,0.0000,0.0000,1.0000$color=1,2,3');
});
it ('Parameter list parser', function () {
@ -22,22 +25,31 @@ describe ('Parameter List', function () {
new OV.Coord3D (1.0, 1.0, 1.0),
new OV.Coord3D (0.0, 0.0, 0.0),
new OV.Coord3D (0.0, 0.0, 1.0)
);
);
let color = new OV.Color (1, 2, 3);
let urlParamsLegacy = 'a.txt,b.txt';
let urlParams1 = 'model=a.txt,b.txt';
let urlParams2 = 'camera=1.0000,1.0000,1.0000,0.0000,0.0000,0.0000,0.0000,0.0000,1.0000';
let urlParams3 = 'model=a.txt,b.txt$camera=1.0000,1.0000,1.0000,0.0000,0.0000,0.0000,0.0000,0.0000,1.0000';
let urlParams4 = 'model=a.txt,b.txt$camera=1.0000,1.0000,1.0000,0.0000,0.0000,0.0000,0.0000,0.0000,1.0000$color=1,2,3';
let parserLegacy = OV.CreateUrlParser (urlParamsLegacy);
assert.deepStrictEqual (parserLegacy.GetModelUrls (), modelUrls);
assert.deepStrictEqual (parserLegacy.GetCamera (), null);
let parser1 = OV.CreateUrlParser (urlParams1);
assert.deepStrictEqual (parser1.GetModelUrls (), modelUrls);
assert.deepStrictEqual (parser1.GetCamera (), null);
assert.deepStrictEqual (parser1.GetColor (), null);
let parser2 = OV.CreateUrlParser (urlParams2);
assert.deepStrictEqual (parser2.GetModelUrls (), null);
assert.deepStrictEqual (parser2.GetCamera (), camera);
assert.deepStrictEqual (parser2.GetColor (), null);
let parser3 = OV.CreateUrlParser (urlParams3);
assert.deepStrictEqual (parser3.GetModelUrls (), modelUrls);
assert.deepStrictEqual (parser3.GetCamera (), camera);
assert.deepStrictEqual (parser3.GetColor (), null);
let parser4 = OV.CreateUrlParser (urlParams4);
assert.deepStrictEqual (parser4.GetModelUrls (), modelUrls);
assert.deepStrictEqual (parser4.GetCamera (), camera);
assert.deepStrictEqual (parser4.GetColor (), color);
});
});

View File

@ -52,7 +52,7 @@ module.exports =
content = testUtils.GetArrayBufferFileContent (folder, fileName);
}
var extension = OV.GetFileExtension (fileName);
let buffers = new OV.ImporterBuffers (function (filePath) {
let buffers = new OV.ImportBuffers (function (filePath) {
let extension = OV.GetFileExtension (filePath);
let knownFormats = importer.GetKnownFileFormats ();
let format = OV.FileFormat.Binary;

View File

@ -54,6 +54,7 @@
"website/o3dv/treeview.js",
"website/o3dv/modal.js",
"website/o3dv/dialogs.js",
"website/o3dv/exportdialog.js",
"website/o3dv/modeldata.js",
"website/o3dv/info.js",
"website/o3dv/menu.js",

View File

@ -30,6 +30,16 @@
width=360 height=240
style="border:1px solid #eeeeee;">
</iframe>
<iframe
src="../../website/embed.html#model=../test/testfiles/obj/hundred_cubes.obj"
width=360 height=240
style="border:1px solid #eeeeee;">
</iframe>
<iframe
src="../../website/embed.html#model=../test/testfiles/obj/hundred_cubes.obj$color=200,0,0"
width=360 height=240
style="border:1px solid #eeeeee;">
</iframe>
<iframe
src="../../website/embed.html#model=wrong.3ds"
width=360 height=240

View File

@ -89,6 +89,15 @@
model="../../test/testfiles/obj/hundred_cubes.obj,../../test/testfiles/obj/hundred_cubes.mtl"
camera="1,1,1,0,0,0,0,0,1">
</div>
<div class="online_3d_viewer"
style="width: 360px; height: 240px;"
model="../../test/testfiles/obj/hundred_cubes.obj">
</div>
<div class="online_3d_viewer"
style="width: 360px; height: 240px;"
model="../../test/testfiles/obj/hundred_cubes.obj"
color="200,0,0">
</div>
<div class="online_3d_viewer"
style="width: 360px; height: 240px;"
model="wrong.3ds">

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18" xml:space="preserve"><path d="m14.5 17.5 3-3m-3-3 3 3m-5.5 3-3-3m3-3-3 3M1.5 3.4v5.7l5 3 5-3V3.4L6.5.5zm5 8.7V6.2m-5-2.8 5 2.8m0 0 5-2.8" fill="none" stroke="#263238" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10"/></svg>

After

Width:  |  Height:  |  Size: 304 B

View File

@ -68,6 +68,7 @@
<script type="text/javascript" src="o3dv/treeview.js"></script>
<script type="text/javascript" src="o3dv/modal.js"></script>
<script type="text/javascript" src="o3dv/dialogs.js"></script>
<script type="text/javascript" src="o3dv/exportdialog.js"></script>
<script type="text/javascript" src="o3dv/modeldata.js"></script>
<script type="text/javascript" src="o3dv/info.js"></script>
<script type="text/javascript" src="o3dv/menu.js"></script>

View File

@ -68,6 +68,7 @@
<script type="text/javascript" src="o3dv/treeview.js"></script>
<script type="text/javascript" src="o3dv/modal.js"></script>
<script type="text/javascript" src="o3dv/dialogs.js"></script>
<script type="text/javascript" src="o3dv/exportdialog.js"></script>
<script type="text/javascript" src="o3dv/modeldata.js"></script>
<script type="text/javascript" src="o3dv/info.js"></script>
<script type="text/javascript" src="o3dv/menu.js"></script>

View File

@ -1,135 +1,6 @@
OV.ProgressDialog = class
OV.FeatureSet =
{
constructor ()
{
this.modal = new OV.Modal ();
this.modal.SetCloseable (false);
this.imageDiv = null;
this.textDiv = null;
}
SetText (text)
{
this.textDiv.html (text);
}
Show (text)
{
let contentDiv = this.modal.GetContentDiv ();
contentDiv.addClass ('ov_progress');
this.imageDiv = $('<img>').addClass ('ov_progress_img').attr ('src', 'assets/images/3dviewer_net_logo.svg').appendTo (contentDiv);
this.textDiv = $('<div>').addClass ('ov_progress_text').appendTo (contentDiv);
this.SetText (text);
this.modal.Open ();
}
Hide ()
{
this.modal.Close ();
}
};
OV.ButtonDialog = class
{
constructor ()
{
this.modal = new OV.Modal ();
}
Init (title, buttons)
{
function AddButton (button, buttonsDiv)
{
let buttonDiv = $('<div>').addClass ('ov_dialog_button').html (button.name).appendTo (buttonsDiv);
if (button.subClass) {
buttonDiv.addClass (button.subClass);
}
buttonDiv.click (function () {
button.onClick ();
});
}
let contentDiv = this.modal.GetContentDiv ();
contentDiv.addClass ('ov_dialog');
$('<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);
for (let i = 0; i < buttons.length; i++) {
AddButton (buttons[i], buttonsInnerDiv);
}
return dialogContentDiv;
}
SetCloseHandler (closeHandler)
{
this.modal.SetCloseHandler (closeHandler);
}
Show ()
{
this.modal.Open ();
}
Hide ()
{
this.modal.Close ();
}
};
OV.ListPopup = class
{
constructor ()
{
this.modal = new OV.Modal ();
this.listDiv = null;
}
Init ()
{
let contentDiv = this.modal.GetContentDiv ();
contentDiv.addClass ('ov_popup');
this.listDiv = $('<div>').addClass ('ov_popup_list').addClass ('ov_thin_scrollbar').appendTo (contentDiv);
}
SetCustomResizeHandler (customResizeHandler)
{
this.modal.SetCustomResizeHandler (customResizeHandler);
}
AddListItem (item, callbacks)
{
let listItemDiv = $('<div>').addClass ('ov_popup_list_item').appendTo (this.listDiv);
if (item.color) {
$('<div>').addClass ('ov_popup_list_item_rgbbox').css ('background', '#' + item.color).appendTo (listItemDiv);
}
$('<div>').addClass ('ov_popup_list_item_name').html (item.name).appendTo (listItemDiv);
listItemDiv.click (callbacks.onClick);
if (OV.IsHoverEnabled () && callbacks.onHoverStart && callbacks.onHoverStop) {
listItemDiv.hover (
function () {
callbacks.onHoverStart ();
},
function () {
callbacks.onHoverStop ();
}
);
}
}
Show ()
{
this.modal.Open ();
}
Hide ()
{
this.modal.Close ();
}
SetDefaultColor : false
};
OV.ShowMessageDialog = function (title, message, subMessage)
@ -206,94 +77,30 @@ OV.ShowOpenUrlDialog = function (onOk)
return dialog;
};
OV.ShowExportDialog = function (model)
OV.ShowEmbeddingDialog = function (importer, importSettings, camera)
{
if (model === null) {
return OV.ShowMessageDialog ('Export Failed', 'Please load a model to export', null);
}
let dialog = new OV.ButtonDialog ();
let contentDiv = dialog.Init ('Export', [
{
name : 'Close',
onClick () {
dialog.Hide ();
}
}
]);
let text = 'Select a format from the below list to export your model. Please note that the export can take several second.';
$('<div>').html (text).addClass ('ov_dialog_section').appendTo (contentDiv);
let formats = [
{ 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 (text)', format : OV.FileFormat.Text, extension : 'ply' },
{ name : 'ply (binary)', format : OV.FileFormat.Binary, extension : 'ply' },
{ 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);
$('<option>').html ('Select format').appendTo (formatSelect);
for (let i = 0; i < formats.length; i++) {
let format = formats[i];
$('<option>').html (format.name).appendTo (formatSelect);
}
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');
if (selectedIndex < 1) {
return;
}
let selectedFormat = formats[selectedIndex - 1];
fileList.html ('Please wait...');
OV.RunTaskAsync (function () {
let exporter = new OV.Exporter ();
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) {
url = OV.CreateObjectUrl (file.GetContent ());
createdUrls.push (url);
}
let fileLink = $('<a>').addClass ('ov_dialog_file_link').appendTo (fileList);
fileLink.attr ('href', url);
fileLink.attr ('download', file.GetName ());
$('<img>').addClass ('ov_dialog_file_link_icon').attr ('src', 'assets/images/dialog/file_download.svg').appendTo (fileLink);
$('<div>').addClass ('ov_dialog_file_link_text').html (file.GetName ()).appendTo (fileLink);
}
});
});
dialog.SetCloseHandler (function () {
for (let i = 0; i < createdUrls.length; i++) {
OV.RevokeObjectUrl (createdUrls[i]);
}
});
dialog.Show ();
return dialog;
};
OV.ShowEmbeddingDialog = function (importer, camera)
{
function GetEmbeddingCode (files, camera, useCameraCheck)
function AddCheckboxLine (parentDiv, text, onChange)
{
let line = $('<div>').addClass ('ov_dialog_table_row').appendTo (parentDiv);
let check = $('<input>').attr ('type', 'checkbox').attr ('checked', 'true').appendTo (line);
$('<span>').html (text).appendTo (line);
check.change (function () {
onChange (check.prop ('checked'));
});
}
function GetEmbeddingCode (params)
{
let builder = OV.CreateUrlBuilder ();
builder.AddModelUrls (params.files);
builder.AddCamera (params.camera);
builder.AddColor (params.color);
let hashParameters = builder.GetParameterList ();
let embeddingCode = '';
embeddingCode += '<iframe';
embeddingCode += ' width="640" height="480"';
embeddingCode += ' style="border:1px solid #eeeeee;"';
let hashParameters = OV.CreateUrlParameters (files, useCameraCheck.get (0).checked ? camera : null);
embeddingCode += ' src="https://3dviewer.net/embed.html#' + hashParameters + '">';
embeddingCode += '</iframe>';
return embeddingCode;
@ -314,6 +121,12 @@ OV.ShowEmbeddingDialog = function (importer, camera)
modelFiles.push (file.fileUrl);
}
let embeddingParams = {
files : modelFiles,
camera : camera,
color : null
};
let dialog = new OV.ButtonDialog ();
let urlsTextArea = $('<textarea>').attr ('readonly', 'true').addClass ('ov_dialog_textarea');
let contentDiv = dialog.Init ('Embedding', [
@ -324,19 +137,25 @@ OV.ShowEmbeddingDialog = function (importer, camera)
}
}
]);
let text = 'Embedding options:';
$('<div>').html (text).addClass ('ov_dialog_section').appendTo (contentDiv);
let optionsSection = $('<div>').addClass ('ov_dialog_section').appendTo (contentDiv);
let useCameraLine = $('<div>').appendTo (optionsSection);
let useCamera = $('<input>').attr ('type', 'checkbox').attr ('checked', 'true').appendTo (useCameraLine);
$('<span>').html ('Use current camera position').appendTo (useCameraLine);
useCamera.change (function () {
let newEmbeddingCode = GetEmbeddingCode (modelFiles, camera, useCamera);
urlsTextArea.val (newEmbeddingCode);
AddCheckboxLine (optionsSection, 'Use current camera position', function (checked) {
embeddingParams.camera = checked ? camera : null;
urlsTextArea.val (GetEmbeddingCode (embeddingParams));
});
let embeddingCode = GetEmbeddingCode (modelFiles, camera, useCamera);
urlsTextArea.val (embeddingCode);
if (OV.FeatureSet.SetDefaultColor) {
AddCheckboxLine (optionsSection, 'Use overridden default color', function (checked) {
embeddingParams.color = checked ? importSettings.defaultColor : null;
urlsTextArea.val (GetEmbeddingCode (embeddingParams));
});
embeddingParams.color = importSettings.defaultColor;
}
urlsTextArea.val (GetEmbeddingCode (embeddingParams));
urlsTextArea.appendTo (contentDiv);
let copyToClipboardText = 'copy to clipboard';
@ -359,6 +178,42 @@ OV.ShowEmbeddingDialog = function (importer, camera)
return dialog;
};
OV.ShowSettingsDialog = function (importSettings, onOk)
{
let dialogSettings = {
defaultColor : importSettings.defaultColor
};
let dialog = new OV.ButtonDialog ();
let contentDiv = dialog.Init ('Settings', [
{
name : 'Cancel',
subClass : 'outline',
onClick () {
dialog.Hide ();
}
},
{
name : 'OK',
onClick () {
dialog.Hide ();
onOk (dialogSettings);
}
}
]);
let colorRow = $('<div>').addClass ('ov_dialog_table_row').appendTo (contentDiv);
$('<div>').html ('Default Color').addClass ('ov_dialog_table_row_name').appendTo (colorRow);
let valueColumn = $('<div>').addClass ('ov_dialog_table_row_value').appendTo (colorRow);
let colorInput = $('<input>').attr ('type', 'color').addClass ('ov_dialog_color').appendTo (valueColumn);
colorInput.val ('#' + OV.ColorToHexString (dialogSettings.defaultColor));
colorInput.change (function () {
let colorStr = colorInput.val ().substr (1);
dialogSettings.defaultColor = OV.HexStringToColor (colorStr);
});
dialog.Show ();
return dialog;
};
OV.ShowListPopup = function (button, items, callbacks)
{
let popup = new OV.ListPopup ();

View File

@ -20,8 +20,13 @@ OV.Embed = class
if (urls === null) {
return;
}
this.modelLoader.LoadFromUrlList (urls);
let hashParameters = OV.CreateUrlParameters (urls, null);
let settings = new OV.ImportSettings ();
let color = this.hashHandler.GetColorFromHash ();
if (color !== null) {
settings.defaultColor = color;
}
this.modelLoader.LoadFromUrlList (urls, settings);
let hashParameters = OV.CreateModelUrlParameters (urls);
let websiteUrl = this.parameters.websiteLinkDiv.attr ('href') + '#' + hashParameters;
this.parameters.websiteLinkDiv.attr ('href', websiteUrl);
}

View File

@ -0,0 +1,191 @@
OV.ExportDialog = class
{
constructor (callbacks)
{
this.callbacks = callbacks;
this.model = null;
this.exportFormats = [
{
name : 'obj',
formats : [
{ name : 'text', format : OV.FileFormat.Text, extension : 'obj' }
]
},
{
name : 'stl',
formats : [
{ name : 'text', format : OV.FileFormat.Text, extension : 'stl' },
{ name : 'binary', format : OV.FileFormat.Binary, extension : 'stl' }
]
},
{
name : 'ply',
formats : [
{ name : 'text', format : OV.FileFormat.Text, extension : 'ply' },
{ name : 'binary', format : OV.FileFormat.Binary, extension : 'ply' }
]
},
{
name : 'gltf',
formats : [
{ name : 'text', format : OV.FileFormat.Text, extension : 'gltf' },
{ name : 'binary', format : OV.FileFormat.Binary, extension : 'glb' }
]
},
{
name : 'off',
formats : [
{ name : 'text', format : OV.FileFormat.Text, extension : 'off' }
]
}
];
this.formatParameters = {
exportFormatButtonDivs : [],
formatSettingsDiv : null,
selectedFormat : null
};
}
Show (model)
{
if (model === null) {
let messageDialog = OV.ShowMessageDialog (
'Export Failed',
'Please load a model before exporting.',
null
);
this.callbacks.onDialog (messageDialog);
return;
}
let obj = this;
let mainDialog = new OV.ButtonDialog ();
let contentDiv = mainDialog.Init ('Export', [
{
name : 'Close',
subClass : 'outline',
onClick () {
mainDialog.Hide ();
}
},
{
name : 'Export',
onClick () {
let selectedFormat = obj.formatParameters.selectedFormat;
if (selectedFormat === null) {
return;
}
mainDialog.Hide ();
obj.ExportFormat (model);
}
}
]);
let text = 'Select a format from the below list to export your model. Please note that the export can take several second.';
$('<div>').html (text).addClass ('ov_dialog_section').appendTo (contentDiv);
let buttonWidth = 40;
let optionsHeight = 50;
let exportFormatSelect = $('<div>').addClass ('ov_dialog_select').appendTo (contentDiv);
this.formatParameters.formatSettingsDiv = $('<div>').addClass ('ov_dialog_section').height (optionsHeight).appendTo (contentDiv);
for (let i = 0; i < this.exportFormats.length; i++) {
let exportFormat = this.exportFormats[i];
let exportFormatButton = $('<div>').addClass ('ov_dialog_select_option').html (exportFormat.name).width (buttonWidth).appendTo (exportFormatSelect);
this.formatParameters.exportFormatButtonDivs.push (exportFormatButton);
exportFormatButton.click (function () {
obj.OnExportFormatSelect (i);
});
}
this.OnExportFormatSelect (0);
mainDialog.Show ();
this.callbacks.onDialog (mainDialog);
}
OnExportFormatSelect (exportFormatIndex)
{
this.formatParameters.formatSettingsDiv.empty ();
for (let i = 0; i < this.formatParameters.exportFormatButtonDivs.length; i++) {
let exportFormatButtonDiv = this.formatParameters.exportFormatButtonDivs[i];
if (i === exportFormatIndex) {
exportFormatButtonDiv.addClass ('selected');
} else {
exportFormatButtonDiv.removeClass ('selected');
}
}
let obj = this;
let exportFormat = this.exportFormats[exportFormatIndex];
for (let i = 0; i < exportFormat.formats.length; i++) {
let format = exportFormat.formats[i];
let formatDiv = $('<div>').addClass ('ov_dialog_table_row').appendTo (this.formatParameters.formatSettingsDiv);
let formatInput = $('<input>').addClass ('ov_dialog_table_radio').attr ('type', 'radio').attr ('id', format.name).attr ('name', 'format').appendTo (formatDiv);
$('<label>').attr ('for', format.name).html (format.name).appendTo (formatDiv);
if (i === 0) {
formatInput.prop ('checked', true);
this.formatParameters.selectedFormat = format;
}
formatInput.change (function () {
obj.formatParameters.selectedFormat = format;
});
}
}
ExportFormat (model)
{
let format = this.formatParameters.selectedFormat;
if (format === null) {
return;
}
let obj = this;
let progressDialog = new OV.ProgressDialog ();
progressDialog.Show ('Exporting Model');
OV.RunTaskAsync (function () {
let exporter = new OV.Exporter ();
let files = exporter.Export (model, format.format, format.extension);
if (files.length === 0) {
progressDialog.Hide ();
} else if (files.length === 1) {
progressDialog.Hide ();
let file = files[0];
OV.DownloadArrayBufferAsFile (file.GetContent (), file.GetName ());
} else if (files.length > 1) {
progressDialog.Hide ();
obj.ShowExportedFiles (files);
}
});
}
ShowExportedFiles (files)
{
let dialog = new OV.ButtonDialog ();
let contentDiv = dialog.Init ('Exported Files', [
{
name : 'Close',
onClick () {
dialog.Hide ();
}
}
]);
let text = 'You can download your exported files here.';
$('<div>').html (text).addClass ('ov_dialog_section').appendTo (contentDiv);
let fileListSection = $('<div>').addClass ('ov_dialog_section').appendTo (contentDiv);
let fileList = $('<div>').addClass ('ov_dialog_file_list').addClass ('ov_thin_scrollbar').appendTo (fileListSection);
for (let i = 0; i < files.length; i++) {
let file = files[i];
let url = OV.CreateObjectUrl (file.GetContent ());
let fileLink = $('<a>').addClass ('ov_dialog_file_link').appendTo (fileList);
fileLink.attr ('href', url);
fileLink.attr ('download', file.GetName ());
$('<img>').addClass ('ov_dialog_file_link_icon').attr ('src', 'assets/images/dialog/file_download.svg').appendTo (fileLink);
$('<div>').addClass ('ov_dialog_file_link_text').html (file.GetName ()).appendTo (fileLink);
}
dialog.Show ();
this.callbacks.onDialog (dialog);
}
};

View File

@ -28,12 +28,6 @@ OV.HashHandler = class
this.SetHash ('');
}
GetCameraFromHash ()
{
let parser = OV.CreateUrlParser (this.GetHash ());
return parser.GetCamera ();
}
GetModelFilesFromHash ()
{
let parser = OV.CreateUrlParser (this.GetHash ());
@ -42,10 +36,22 @@ OV.HashHandler = class
SetModelFilesToHash (files)
{
let params = OV.CreateUrlParameters (files, null);
let params = OV.CreateModelUrlParameters (files);
this.SetHash (params);
}
GetCameraFromHash ()
{
let parser = OV.CreateUrlParser (this.GetHash ());
return parser.GetCamera ();
}
GetColorFromHash ()
{
let parser = OV.CreateUrlParser (this.GetHash ());
return parser.GetColor ();
}
GetHash ()
{
return window.location.hash.substr (1);

View File

@ -51,7 +51,7 @@ OV.InfoPanel = class
let infoContainer = $('<div>').addClass ('ov_info_box').appendTo (contentDiv);
AddRow (infoContainer, 'Color', function (valueDiv) {
let colorString = '#' + info.diffuse.ToHexString ();
let colorString = '#' + OV.ColorToHexString (info.diffuse);
$('<div>').addClass ('ov_info_box_rgbbox').css ('background', colorString).attr ('title', colorString).appendTo (valueDiv);
$('<div>').addClass ('ov_info_box_rgbtext').html (colorString).attr ('title', colorString).appendTo (valueDiv);
});
@ -128,7 +128,7 @@ OV.InfoPanel = class
let materialInfo = getMaterialInfo (info.usedMaterials[i]);
materialItems.push ({
name : OV.GetMaterialName (materialInfo.name),
color : materialInfo.diffuse.ToHexString ()
color : OV.ColorToHexString (materialInfo.diffuse)
});
}

View File

@ -97,3 +97,137 @@ OV.Modal = class
}
}
};
OV.ProgressDialog = class
{
constructor ()
{
this.modal = new OV.Modal ();
this.modal.SetCloseable (false);
this.imageDiv = null;
this.textDiv = null;
}
SetText (text)
{
this.textDiv.html (text);
}
Show (text)
{
let contentDiv = this.modal.GetContentDiv ();
contentDiv.addClass ('ov_progress');
this.imageDiv = $('<img>').addClass ('ov_progress_img').attr ('src', 'assets/images/3dviewer_net_logo.svg').appendTo (contentDiv);
this.textDiv = $('<div>').addClass ('ov_progress_text').appendTo (contentDiv);
this.SetText (text);
this.modal.Open ();
}
Hide ()
{
this.modal.Close ();
}
};
OV.ButtonDialog = class
{
constructor ()
{
this.modal = new OV.Modal ();
}
Init (title, buttons)
{
function AddButton (button, buttonsDiv)
{
let buttonDiv = $('<div>').addClass ('ov_dialog_button').html (button.name).appendTo (buttonsDiv);
if (button.subClass) {
buttonDiv.addClass (button.subClass);
}
buttonDiv.click (function () {
button.onClick ();
});
}
let contentDiv = this.modal.GetContentDiv ();
contentDiv.addClass ('ov_dialog');
$('<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);
for (let i = 0; i < buttons.length; i++) {
AddButton (buttons[i], buttonsInnerDiv);
}
return dialogContentDiv;
}
SetCloseHandler (closeHandler)
{
this.modal.SetCloseHandler (closeHandler);
}
Show ()
{
this.modal.Open ();
}
Hide ()
{
this.modal.Close ();
}
};
OV.ListPopup = class
{
constructor ()
{
this.modal = new OV.Modal ();
this.listDiv = null;
}
Init ()
{
let contentDiv = this.modal.GetContentDiv ();
contentDiv.addClass ('ov_popup');
this.listDiv = $('<div>').addClass ('ov_popup_list').addClass ('ov_thin_scrollbar').appendTo (contentDiv);
}
SetCustomResizeHandler (customResizeHandler)
{
this.modal.SetCustomResizeHandler (customResizeHandler);
}
AddListItem (item, callbacks)
{
let listItemDiv = $('<div>').addClass ('ov_popup_list_item').appendTo (this.listDiv);
if (item.color) {
$('<div>').addClass ('ov_popup_list_item_rgbbox').css ('background', '#' + item.color).appendTo (listItemDiv);
}
$('<div>').addClass ('ov_popup_list_item_name').html (item.name).appendTo (listItemDiv);
listItemDiv.click (callbacks.onClick);
if (OV.IsHoverEnabled () && callbacks.onHoverStart && callbacks.onHoverStop) {
listItemDiv.hover (
function () {
callbacks.onHoverStart ();
},
function () {
callbacks.onHoverStop ();
}
);
}
}
Show ()
{
this.modal.Open ();
}
Hide ()
{
this.modal.Close ();
}
};

View File

@ -100,6 +100,16 @@ OV.CopyToClipboard = function (text)
document.body.removeChild (input);
};
OV.DownloadArrayBufferAsFile = function (arrayBuffer, fileName)
{
let link = document.createElement ('a');
link.href = OV.CreateObjectUrl (arrayBuffer);
link.download = fileName;
document.body.appendChild (link);
link.click ();
document.body.removeChild (link);
};
OV.CreateIconButton = function (iconName, hoverIconName, title, link)
{
let buttonLink = $('<a>');

View File

@ -449,23 +449,34 @@ div.ov_dialog textarea.ov_dialog_textarea
box-sizing: border-box;
}
div.ov_dialog select.ov_dialog_select
div.ov_dialog div.ov_dialog_select
{
background-image: url('../assets/images/dialog/arrow_down.svg');
background-repeat: no-repeat;
background-size: 18px 18px;
background-position: right 10px center;
font-size: 14px;
margin: 10px 0px;
width: 100%;
padding: 10px;
border: 1px solid #cccccc;
margin: 20px 0px;
overflow: auto;
}
div.ov_dialog div.ov_dialog_select_option
{
color: #3393bd;
text-align: center;
padding: 3px;
margin-right: 5px;
border: 1px solid #3393bd;
border-radius: 5px;
outline: none;
box-sizing: border-box;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
cursor: pointer;
float: left;
}
div.ov_dialog div.ov_dialog_select_option.selected
{
color: #ffffff;
background: #3393bd;
}
div.ov_dialog input.ov_dialog_color
{
width: 36px;
height: 18px;
}
div.ov_dialog div.ov_dialog_file_list
@ -508,6 +519,28 @@ div.ov_dialog div.ov_dialog_inner_button
cursor: pointer;
}
div.ov_dialog div.ov_dialog_table_row
{
padding: 2px 0px;
overflow: auto;
}
div.ov_dialog div.ov_dialog_table_row_name
{
width: 120px;
float: left;
}
div.ov_dialog div.ov_dialog_table_row_value
{
float: left;
}
div.ov_dialog input.ov_dialog_table_radio
{
margin-right: 10px;
}
div.ov_popup
{
background: #ffffff;

View File

@ -7,6 +7,7 @@ OV.Website = class
this.hashHandler = new OV.HashHandler ();
this.toolbar = new OV.Toolbar (this.parameters.toolbarDiv);
this.menu = new OV.Menu (this.parameters.menuDiv);
this.importSettings = new OV.ImportSettings ();
this.modelLoader = new OV.ThreeModelLoader ();
this.highlightMaterial = new THREE.MeshPhongMaterial ({
color : 0x8ec9f0,
@ -154,19 +155,24 @@ OV.Website = class
});
}
LoadModelFromUrlList (urls)
LoadModelFromUrlList (urls, settings)
{
this.modelLoader.LoadFromUrlList (urls);
this.modelLoader.LoadFromUrlList (urls, settings);
this.ClearHashIfNotOnlyUrlList ();
}
LoadModelFromFileList (files)
{
if (files.length === 0) {
this.modelLoader.LoadFromFileList (files, this.importSettings);
this.ClearHashIfNotOnlyUrlList ();
}
ReloadFiles ()
{
if (this.model === null) {
return;
}
this.modelLoader.LoadFromFileList (files);
this.ClearHashIfNotOnlyUrlList ();
this.modelLoader.ReloadFiles (this.importSettings);
}
ClearHashIfNotOnlyUrlList ()
@ -186,7 +192,13 @@ OV.Website = class
if (urls === null) {
return;
}
this.LoadModelFromUrlList (urls);
let settings = new OV.ImportSettings ();
settings.defaultColor = this.importSettings.defaultColor;
let color = this.hashHandler.GetColorFromHash ();
if (color !== null) {
settings.defaultColor = color;
}
this.LoadModelFromUrlList (urls, settings);
} else {
this.ClearModel ();
this.parameters.introDiv.show ();
@ -233,6 +245,8 @@ OV.Website = class
}
let obj = this;
let importer = this.modelLoader.GetImporter ();
AddButton (this.toolbar, 'open', 'Open model from your device', false, function () {
obj.OpenFileBrowserDialog ();
});
@ -266,14 +280,36 @@ OV.Website = class
});
AddSeparator (this.toolbar, true);
AddButton (this.toolbar, 'export', 'Export model', true, function () {
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 ());
let exportDialog = new OV.ExportDialog ({
onDialog : function (dialog) {
obj.dialog = dialog;
}
});
exportDialog.Show (obj.model);
});
AddButton (this.toolbar, 'embed', 'Get embedding code', true, function () {
obj.dialog = OV.ShowEmbeddingDialog (importer, obj.importSettings, obj.viewer.GetCamera ());
});
if (OV.FeatureSet.SetDefaultColor) {
AddSeparator (this.toolbar, true);
AddButton (this.toolbar, 'settings', 'Settings', true, function () {
obj.dialog = OV.ShowSettingsDialog (obj.importSettings, function (dialogSettings) {
let reload = false;
if (!OV.ColorIsEqual (obj.importSettings.defaultColor, dialogSettings.defaultColor)) {
obj.importSettings.defaultColor = dialogSettings.defaultColor;
reload = true;
}
if (reload) {
obj.ReloadFiles ();
}
});
});
}
this.parameters.fileInput.on ('change', function (ev) {
obj.LoadModelFromFileList (ev.target.files);
if (ev.target.files.length > 0) {
obj.LoadModelFromFileList (ev.target.files);
}
});
}
@ -293,7 +329,9 @@ OV.Website = class
window.addEventListener ('drop', function (ev) {
ev.stopPropagation ();
ev.preventDefault ();
obj.LoadModelFromFileList (ev.dataTransfer.files);
if (ev.dataTransfer.files.length > 0) {
obj.LoadModelFromFileList (ev.dataTransfer.files);
}
}, false);
}