ExtensionInterface = function (app) { this.app = app; }; ExtensionInterface.prototype.GetButtonsDiv = function () { return this.app.extensionButtons.GetButtonsDiv (); }; ExtensionInterface.prototype.GetModelJson = function () { return this.app.viewer.GetJsonData (); }; ImporterApp = function () { this.viewer = null; this.fileNames = null; this.inGenerate = false; this.extensions = []; this.importerButtons = null; this.extensionButtons = null; this.dialog = null; this.readyForTest = null; }; ImporterApp.prototype.Init = function () { if (!JSM.IsWebGLEnabled () || !JSM.IsFileApiEnabled ()) { while (document.body.lastChild) { document.body.removeChild (document.body.lastChild); } var div = $('
').addClass ('nosupport').appendTo ($('body')); div.html ([ '
', this.GetWelcomeText (), '
You need a browser which supports the following technologies: WebGL, WebGLRenderingContext, File, FileReader, FileList, Blob, URL.
', '
' ].join ('')); return; } var myThis = this; var top = document.getElementById ('top'); this.importerButtons = new ImporterButtons (top); this.importerButtons.AddLogo ('Online 3D Viewer v 0.5.1', function () { myThis.WelcomeDialog (); }); this.importerButtons.AddButton ('images/openfile.png', 'Open File', function () { myThis.OpenFile (); }); this.importerButtons.AddButton ('images/fitinwindow.png', 'Fit In Window', function () { myThis.FitInWindow (); }); this.importerButtons.AddToggleButton ('images/fixup.png', 'images/fixupgray.png', 'Enable/Disable Fixed Up Vector', function () { myThis.SetFixUp (); }); this.importerButtons.AddButton ('images/top.png', 'Set Up Vector (Z)', function () { myThis.SetNamedView ('z'); }); this.importerButtons.AddButton ('images/bottom.png', 'Set Up Vector (-Z)', function () { myThis.SetNamedView ('-z'); }); this.importerButtons.AddButton ('images/front.png', 'Set Up Vector (Y)', function () { myThis.SetNamedView ('y'); }); this.importerButtons.AddButton ('images/back.png', 'Set Up Vector (-Y)', function () { myThis.SetNamedView ('-y'); }); this.importerButtons.AddButton ('images/left.png', 'Set Up Vector (X)', function () { myThis.SetNamedView ('x'); }); this.importerButtons.AddButton ('images/right.png', 'Set Up Vector (-X)', function () { myThis.SetNamedView ('-x'); }); this.extensionButtons = new ExtensionButtons (top); this.dialog = new FloatingDialog (); window.addEventListener ('resize', this.Resize.bind (this), false); this.Resize (); this.viewer = new ImporterViewer (); this.viewer.Init ('example'); window.addEventListener ('dragover', this.DragOver.bind (this), false); window.addEventListener ('drop', this.Drop.bind (this), false); var fileInput = document.getElementById ('file'); fileInput.addEventListener ('change', this.FileSelected.bind (this), false); var testMode = this.InitTestMode (); var hasHashModel = false; if (!testMode) { window.onhashchange = this.LoadFilesFromHash.bind (this); hasHashModel = this.LoadFilesFromHash (); } if (!hasHashModel) { this.WelcomeDialog (); } }; ImporterApp.prototype.ClearReadyForTest = function () { if (this.readyForTest !== null) { this.readyForTest.remove (); this.readyForTest = null; } }; ImporterApp.prototype.SetReadyForTest = function () { this.readyForTest = $('
').attr ('id', 'readyfortest').hide ().appendTo ($('body')); }; ImporterApp.prototype.AddExtension = function (extension) { if (!extension.IsEnabled ()) { return; } var extInterface = new ExtensionInterface (this); extension.Init (extInterface); }; ImporterApp.prototype.WelcomeDialog = function () { var dialogText = [ '
', this.GetWelcomeText (), '
', ].join (''); this.dialog.Open ({ title : 'Welcome', text : dialogText, buttons : [ { text : 'ok', callback : function (dialog) { dialog.Close (); } } ] }); }; ImporterApp.prototype.GetWelcomeText = function () { var welcomeText = [ '
Welcome to Online 3D Viewer!
', '
Here you can view your local 3D models online. You have three ways to open a file. Use the open button above to select files, simply drag and drop files to this browser window, or define the url of the files as location hash.
', '
Supported formats: 3ds, obj, stl.
', '
Powered by Three.js and JSModeler.
', '
', ].join (''); return welcomeText; }; ImporterApp.prototype.Resize = function () { function SetWidth (elem, value) { elem.width = value; elem.style.width = value + 'px'; } function SetHeight (elem, value) { elem.height = value; elem.style.height = value + 'px'; } var top = document.getElementById ('top'); var left = document.getElementById ('left'); var canvas = document.getElementById ('example'); var height = document.body.clientHeight - top.offsetHeight; SetHeight (canvas, 0); SetWidth (canvas, 0); SetHeight (left, height); SetHeight (canvas, height); SetWidth (canvas, document.body.clientWidth - left.offsetWidth); this.dialog.Resize (); }; ImporterApp.prototype.JsonLoaded = function (progressBar) { var jsonData = this.viewer.GetJsonData (); this.meshVisibility = {}; var i; for (i = 0; i < jsonData.meshes.length; i++) { this.meshVisibility[i] = true; } this.Generate (progressBar); }; ImporterApp.prototype.GenerateMenu = function () { function AddDefaultGroup (menu, name, id) { var group = menu.AddGroup (name, { id : id, openCloseButton : { isOpen : false, open : 'images/opened.png', close : 'images/closed.png', title : 'Show/Hide ' + name } }); return group; } function AddInformation (infoGroup, jsonData) { var infoTable = new InfoTable (infoGroup); var materialCount = jsonData.materials.length; var vertexCount = 0; var triangleCount = 0; var i, j, mesh, triangles; for (i = 0; i < jsonData.meshes.length; i++) { mesh = jsonData.meshes[i]; vertexCount += mesh.vertices.length / 3; for (j = 0; j < mesh.triangles.length; j++) { triangles = mesh.triangles[j]; triangleCount += triangles.parameters.length / 9; } } infoTable.AddRow ('Material count', materialCount); infoTable.AddRow ('Vertex count', vertexCount); infoTable.AddRow ('Triangle count', triangleCount); } function AddMaterial (importerMenu, materialsGroup, material) { importerMenu.AddSubItem (materialsGroup, material.name, { openCloseButton : { isOpen : false, open : 'images/info.png', close : 'images/info.png', onOpen : function (content, material) { var table = new InfoTable (content); table.AddColorRow ('Ambient', material.ambient); table.AddColorRow ('Diffuse', material.diffuse); table.AddColorRow ('Specular', material.specular); table.AddRow ('Shininess', material.shininess.toFixed (2)); table.AddRow ('Opacity', material.opacity.toFixed (2)); }, title : 'Show/Hide Information', userData : material } }); } function AddMesh (importerApp, importerMenu, meshesGroup, mesh, meshIndex) { importerMenu.AddSubItem (meshesGroup, mesh.name, { openCloseButton : { isOpen : false, open : 'images/info.png', close : 'images/info.png', onOpen : function (contentDiv, mesh) { contentDiv.empty (); var table = new InfoTable (contentDiv); var min = new JSM.Coord (JSM.Inf, JSM.Inf, JSM.Inf); var max = new JSM.Coord (-JSM.Inf, -JSM.Inf, -JSM.Inf); var i, vertex; for (i = 0; i < mesh.vertices.length; i = i + 3) { vertex = new JSM.Coord (mesh.vertices[i], mesh.vertices[i + 1], mesh.vertices[i + 2]); min.x = JSM.Minimum (min.x, vertex.x); min.y = JSM.Minimum (min.y, vertex.y); min.z = JSM.Minimum (min.z, vertex.z); max.x = JSM.Maximum (max.x, vertex.x); max.y = JSM.Maximum (max.y, vertex.y); max.z = JSM.Maximum (max.z, vertex.z); } table.AddRow ('X Size', (max.x - min.x).toFixed (2)); table.AddRow ('Y Size', (max.y - min.y).toFixed (2)); table.AddRow ('Z Size', (max.z - min.z).toFixed (2)); var triangleCount = 0; var triangles; for (i = 0; i < mesh.triangles.length; i++) { triangles = mesh.triangles[i]; triangleCount += triangles.parameters.length / 9; } table.AddRow ('Vertex count', mesh.vertices.length / 3); table.AddRow ('Triangle count', triangleCount); }, title : 'Show/Hide Information', userData : mesh }, userButton : { id : 'showhidemesh-' + meshIndex, onCreate : function (image) { image.attr ('src', 'images/visible.png'); }, onClick : function (image, meshIndex) { var visible = importerApp.ShowHideMesh (meshIndex); image.attr ('src', visible ? 'images/visible.png' : 'images/hidden.png'); }, title : 'Show/Hide Mesh', userData : meshIndex } }); } var jsonData = this.viewer.GetJsonData (); var menu = $('#menu'); var importerMenu = new ImporterMenu (menu); var filesGroup = AddDefaultGroup (importerMenu, 'Files', 'filesmenuitem'); importerMenu.AddSubItem (filesGroup, this.fileNames.main); var i; for (i = 0; i < this.fileNames.requested.length; i++) { importerMenu.AddSubItem (filesGroup, this.fileNames.requested[i]); } if (this.fileNames.missing.length > 0) { var missingFilesGroup = AddDefaultGroup (importerMenu, 'Missing Files', 'missingfilesmenuitem'); for (i = 0; i < this.fileNames.missing.length; i++) { importerMenu.AddSubItem (missingFilesGroup, this.fileNames.missing[i]); } } var infoGroup = AddDefaultGroup (importerMenu, 'Information', 'informationmenuitem'); AddInformation (infoGroup, jsonData); var materialsGroup = AddDefaultGroup (importerMenu, 'Materials', 'materialsmenuitem'); var material; for (i = 0; i < jsonData.materials.length; i++) { material = jsonData.materials[i]; AddMaterial (importerMenu, materialsGroup, material); } var meshesGroup = AddDefaultGroup (importerMenu, 'Meshes', 'meshesmenuitem'); var mesh; for (i = 0; i < jsonData.meshes.length; i++) { mesh = jsonData.meshes[i]; AddMesh (this, importerMenu, meshesGroup, mesh, i); } }; ImporterApp.prototype.GenerateError = function (errorMessage) { this.viewer.RemoveMeshes (); var menu = $('#menu'); menu.empty (); this.dialog.Open ({ title : 'Error', text : '
' + errorMessage + '
', buttons : [ { text : 'ok', callback : function (dialog) { dialog.Close (); } } ] }); }; ImporterApp.prototype.Generate = function (progressBar) { function ShowMeshes (importerApp, progressBar, merge) { importerApp.inGenerate = true; var environment = { onStart : function (taskCount) { progressBar.Init (taskCount); }, onProgress : function (currentTask) { progressBar.Step (currentTask + 1); }, onFinish : function () { importerApp.GenerateMenu (); importerApp.inGenerate = false; importerApp.SetReadyForTest (); } }; if (merge) { var jsonData = importerApp.viewer.GetJsonData (); importerApp.viewer.SetJsonData (JSM.MergeJsonDataMeshes (jsonData)); } importerApp.viewer.ShowAllMeshes (environment); } var jsonData = this.viewer.GetJsonData (); if (jsonData.materials.length === 0 || jsonData.meshes.length === 0) { this.GenerateError ('Failed to open file. Maybe something is wrong with your file.'); this.SetReadyForTest (); return; } var myThis = this; if (jsonData.meshes.length > 250) { this.dialog.Open ({ title : 'Information', text : '
The model contains a large number of meshes. It can cause performance problems. Would you like to merge meshes?
', buttons : [ { text : 'yes', callback : function (dialog) { ShowMeshes (myThis, progressBar, true); dialog.Close (); } }, { text : 'no', callback : function (dialog) { ShowMeshes (myThis, progressBar, false); dialog.Close (); } } ] }); } else { ShowMeshes (myThis, progressBar, false); } }; ImporterApp.prototype.FitInWindow = function () { this.viewer.FitInWindow (); }; ImporterApp.prototype.SetFixUp = function () { this.viewer.SetFixUp (); }; ImporterApp.prototype.SetNamedView = function (viewName) { this.viewer.SetNamedView (viewName); }; ImporterApp.prototype.SetView = function (viewType) { this.viewer.SetView (viewType); }; ImporterApp.prototype.ShowHideMesh = function (meshIndex) { this.meshVisibility[meshIndex] = !this.meshVisibility[meshIndex]; if (this.meshVisibility[meshIndex]) { this.viewer.ShowMesh (meshIndex); } else { this.viewer.HideMesh (meshIndex); } return this.meshVisibility[meshIndex]; }; ImporterApp.prototype.ProcessFiles = function (fileList, isUrl) { this.ClearReadyForTest (); this.dialog.Close (); if (this.inGenerate) { return; } var userFiles = fileList; if (userFiles.length === 0) { return; } this.fileNames = null; var myThis = this; var processorFunc = JSM.ConvertFileListToJsonData; if (isUrl) { processorFunc = JSM.ConvertURLListToJsonData; } var menu = $('#menu'); menu.empty (); if (isUrl) { menu.html ('Downloading files...'); } else { menu.html ('Loading files...'); } processorFunc (userFiles, { onError : function () { myThis.GenerateError ('No readable file found. You can open 3ds, obj and stl files.'); myThis.SetReadyForTest (); return; }, onReady : function (fileNames, jsonData) { myThis.fileNames = fileNames; myThis.viewer.SetJsonData (jsonData); menu.empty (); var progressBar = new ImporterProgressBar (menu); myThis.JsonLoaded (progressBar); } }); }; ImporterApp.prototype.DragOver = function (event) { event.stopPropagation (); event.preventDefault (); event.dataTransfer.dropEffect = 'copy'; }; ImporterApp.prototype.Drop = function (event) { event.stopPropagation (); event.preventDefault (); this.ResetHash (); this.ProcessFiles (event.dataTransfer.files, false); }; ImporterApp.prototype.FileSelected = function (event) { event.stopPropagation (); event.preventDefault (); this.ResetHash (); this.ProcessFiles (event.target.files, false); }; ImporterApp.prototype.OpenFile = function () { var fileInput = document.getElementById ('file'); fileInput.click (); }; ImporterApp.prototype.ResetHash = function () { if (window.location.hash.length > 1) { window.location.hash = ''; } }; ImporterApp.prototype.LoadFilesFromHash = function () { if (window.location.hash.length < 2) { return false; } var hash = window.location.hash; var hash = hash.substr (1, hash.length - 1); var fileList = hash.split (','); this.ProcessFiles (fileList, true); return true; }; ImporterApp.prototype.InitTestMode = function () { if (window.location.hash != '#test') { return false; } var currentTestFile = 0; var myThis = this; JSM.LoadJsonFile ('testfiles_for_test/testfiles.json', function (jsonContent) { window.addEventListener ('keydown', function (event) { var keyCode = event.which; if (keyCode == 84 && currentTestFile < jsonContent.files.length) { event.preventDefault (); myThis.dialog.Close (); myThis.ProcessFiles (jsonContent.files[currentTestFile], true); currentTestFile++; } }, false); }); return true; }; window.onload = function () { var importerApp = new ImporterApp (); importerApp.Init (); // ExtensionIncludes importerApp.AddExtension (new ExampleExtension ()); // ExtensionIncludesEnd };