OV.WebsiteSettings = class { constructor () { this.backgroundColor = new OV.Color (255, 255, 255); this.defaultColor = new OV.Color (200, 200, 200); } LoadFromCookies (cookieHandler) { this.backgroundColor = cookieHandler.GetColorVal ('ov_background_color', new OV.Color (255, 255, 255)); this.defaultColor = cookieHandler.GetColorVal ('ov_default_color', new OV.Color (200, 200, 200)); } SaveToCookies (cookieHandler) { cookieHandler.SetColorVal ('ov_background_color', this.backgroundColor); cookieHandler.SetColorVal ('ov_default_color', this.defaultColor); } }; OV.Website = class { constructor (parameters) { this.parameters = parameters; this.viewer = new OV.Viewer (); this.hashHandler = new OV.HashHandler (); this.cookieHandler = new OV.CookieHandler (); this.toolbar = new OV.Toolbar (this.parameters.toolbarDiv); this.sidebar = new OV.Sidebar (this.parameters.sidebarDiv); this.navigator = new OV.Navigator (this.parameters.navigatorDiv); this.eventHandler = new OV.EventHandler (this.parameters.eventHandler); this.settings = new OV.WebsiteSettings (); this.modelLoader = new OV.ThreeModelLoader (); this.highlightMaterial = new THREE.MeshPhongMaterial ({ color : 0x8ec9f0, side : THREE.DoubleSide }); this.themeHandler = new OV.ThemeHandler (); this.detailsPanel = null; this.settingsPanel = null; this.model = null; this.dialog = null; } Load () { this.settings.LoadFromCookies (this.cookieHandler); this.InitViewer (); this.InitToolbar (); this.InitDragAndDrop (); this.InitModelLoader (); this.InitSidebar (); this.InitNavigator (); this.InitCookieConsent (); this.viewer.SetClickHandler (this.OnModelClicked.bind (this)); this.viewer.SetContextMenuHandler (this.OnModelContextMenu.bind (this)); this.Resize (); this.hashHandler.SetEventListener (this.OnHashChange.bind (this)); this.OnHashChange (); $(window).on ('resize', () => { this.Resize (); }); this.themeHandler.SwitchTheme ('dark'); } Resize () { let windowWidth = $(window).outerWidth (); let windowHeight = $(window).outerHeight (); let headerHeight = parseInt (this.parameters.headerDiv.outerHeight (true), 10); let navigatorWidth = 0; let sidebarWidth = 0; let safetyMargin = 0; if (!OV.IsSmallWidth ()) { navigatorWidth = parseInt (this.parameters.navigatorDiv.outerWidth (true), 10); if (this.sidebar.IsVisible ()) { sidebarWidth = parseInt (this.parameters.sidebarDiv.outerWidth (true), 10); } safetyMargin = 1; } let contentWidth = windowWidth - navigatorWidth - sidebarWidth - safetyMargin; let contentHeight = windowHeight - headerHeight - safetyMargin; this.parameters.navigatorDiv.outerHeight (contentHeight, true); this.parameters.sidebarDiv.outerHeight (contentHeight, true); this.parameters.introDiv.outerHeight (contentHeight, true); this.navigator.Resize (); this.sidebar.Resize (); this.viewer.Resize (contentWidth, contentHeight); } ShowViewer (show) { if (show) { this.parameters.mainDiv.show (); this.Resize (); } else { this.parameters.mainDiv.hide (); } } ClearModel () { this.HidePopups (); this.model = null; this.parameters.introDiv.hide (); this.ShowViewer (false); this.viewer.Clear (); this.navigator.Clear (); } HidePopups () { if (this.dialog !== null) { this.dialog.Hide (); this.dialog = null; } this.sidebar.HidePopups (); } OnModelFinished (importResult, threeMeshes) { this.model = importResult.model; this.ShowViewer (true); this.viewer.AddMeshes (threeMeshes); this.viewer.SetUpVector (importResult.upVector, false); this.navigator.FillTree (importResult); this.settingsPanel.Update (this.model); this.FitModelToWindow (true); } OnModelClicked (button, mouseCoordinates) { if (button === 1) { let meshUserData = this.viewer.GetMeshUserDataUnderMouse (mouseCoordinates); if (meshUserData === null) { this.navigator.SetSelection (null); } else { this.navigator.SetSelection (new OV.Selection (OV.SelectionType.Mesh, meshUserData.originalMeshIndex)); } } } OnModelContextMenu (globalMouseCoordinates, mouseCoordinates) { let meshUserData = this.viewer.GetMeshUserDataUnderMouse (mouseCoordinates); let items = []; if (meshUserData === null) { items.push ({ name : 'Fit model to window', icon : 'fit', onClick : () => { this.FitModelToWindow (false); } }); if (this.navigator.HasHiddenMesh ()) { items.push ({ name : 'Show all meshes', icon : 'visible', onClick : () => { this.navigator.ShowAllMeshes (); } }); } } else { let meshIndex = meshUserData.originalMeshIndex; items.push ({ name : 'Hide mesh', icon : 'hidden', onClick : () => { this.navigator.ToggleMeshVisibility (meshIndex); } }); items.push ({ name : 'Fit mesh to window', icon : 'fit', onClick : () => { this.navigator.FitMeshToWindow (meshIndex); } }); if (this.navigator.MeshCount () > 1) { let isMeshIsolated = this.navigator.IsMeshIsolated (meshIndex); items.push ({ name : isMeshIsolated ? 'Remove isolation' : 'Isolate mesh', icon : isMeshIsolated ? 'deisolate' : 'isolate', onClick : () => { this.navigator.IsolateMesh (meshIndex); } }); } } this.dialog = OV.ShowListPopup (items, { calculatePosition : (contentDiv) => { return OV.CalculatePopupPositionToScreen (globalMouseCoordinates, contentDiv); }, onClick : (index) => { let clickedItem = items[index]; clickedItem.onClick (); } }); } OpenFileBrowserDialog () { this.parameters.fileInput.trigger ('click'); } FitModelToWindow (onLoad) { let animation = !onLoad; let boundingSphere = this.viewer.GetBoundingSphere ((meshUserData) => { return this.navigator.IsMeshVisible (meshUserData.originalMeshIndex); }); if (onLoad) { this.viewer.AdjustClippingPlanesToSphere (boundingSphere); } this.viewer.FitSphereToWindow (boundingSphere, animation); } FitMeshToWindow (meshIndex) { let boundingSphere = this.viewer.GetBoundingSphere ((meshUserData) => { return meshUserData.originalMeshIndex === meshIndex; }); this.viewer.FitSphereToWindow (boundingSphere, true); } UpdateMeshesVisibility () { this.viewer.SetMeshesVisibility ((meshUserData) => { return this.navigator.IsMeshVisible (meshUserData.originalMeshIndex); }); } UpdateMeshesSelection () { let selectedMeshIndex = this.navigator.GetSelectedMeshIndex (); this.viewer.SetMeshesHighlight (this.highlightMaterial, (meshUserData) => { if (meshUserData.originalMeshIndex === selectedMeshIndex) { return true; } return false; }); } LoadModelFromUrlList (urls, settings) { this.modelLoader.LoadFromUrlList (urls, settings); this.ClearHashIfNotOnlyUrlList (); } LoadModelFromFileList (files) { let importSettings = new OV.ImportSettings (); importSettings.defaultColor = this.settings.defaultColor; this.modelLoader.LoadFromFileList (files, importSettings); this.ClearHashIfNotOnlyUrlList (); } ClearHashIfNotOnlyUrlList () { let importer = this.modelLoader.GetImporter (); let isOnlyUrl = importer.IsOnlyFileSource (OV.FileSource.Url); if (!isOnlyUrl && this.hashHandler.HasHash ()) { this.hashHandler.SkipNextEventHandler (); this.hashHandler.ClearHash (); } } OnHashChange () { if (this.hashHandler.HasHash ()) { let urls = this.hashHandler.GetModelFilesFromHash (); if (urls === null) { return; } let importSettings = new OV.ImportSettings (); importSettings.defaultColor = this.settings.defaultColor; let defaultColor = this.hashHandler.GetDefaultColorFromHash (); if (defaultColor !== null) { importSettings.defaultColor = defaultColor; } this.eventHandler.HandleEvent ('model_load_started', { source : 'hash' }); this.LoadModelFromUrlList (urls, importSettings); } else { this.ClearModel (); this.parameters.introDiv.show (); } } InitViewer () { let canvas = $('').appendTo (this.parameters.viewerDiv); this.viewer.Init (canvas.get (0)); this.viewer.SetBackgroundColor (this.settings.backgroundColor); this.viewer.SetEnvironmentMap ([ 'assets/envmaps/fishermans_bastion/posx.jpg', 'assets/envmaps/fishermans_bastion/negx.jpg', 'assets/envmaps/fishermans_bastion/posy.jpg', 'assets/envmaps/fishermans_bastion/negy.jpg', 'assets/envmaps/fishermans_bastion/posz.jpg', 'assets/envmaps/fishermans_bastion/negz.jpg' ]); this.ShowViewer (false); } InitToolbar () { function AddButton (toolbar, eventHandler, imageName, imageTitle, onlyFullWidth, onClick) { let button = toolbar.AddImageButton (imageName, imageTitle, () => { eventHandler.HandleEvent ('toolbar_clicked', { item : imageName }); onClick (); }); if (onlyFullWidth) { button.AddClass ('only_full_width'); } return button; } function AddRadioButton (toolbar, eventHandler, imageNames, imageTitles, selectedIndex, onlyFullWidth, onClick) { let imageData = []; for (let i = 0; i < imageNames.length; i++) { let imageName = imageNames[i]; let imageTitle = imageTitles[i]; imageData.push ({ image : imageName, title : imageTitle }); } let buttons = toolbar.AddImageRadioButton (imageData, selectedIndex, (buttonIndex) => { eventHandler.HandleEvent ('toolbar_clicked', { item : imageNames[buttonIndex] }); onClick (buttonIndex); }); if (onlyFullWidth) { for (let i = 0; i < buttons.length; i++) { let button = buttons[i]; button.AddClass ('only_full_width'); } } } function AddSeparator (toolbar, onlyFullWidth) { let separator = toolbar.AddSeparator (); if (onlyFullWidth) { separator.addClass ('only_full_width'); } } let importer = this.modelLoader.GetImporter (); AddButton (this.toolbar, this.eventHandler, 'open', 'Open model from your device', false, () => { this.OpenFileBrowserDialog (); }); AddButton (this.toolbar, this.eventHandler, 'open_url', 'Open model from a url', false, () => { this.dialog = OV.ShowOpenUrlDialog ((urls) => { if (urls.length > 0) { this.hashHandler.SetModelFilesToHash (urls); } }); }); AddSeparator (this.toolbar); AddButton (this.toolbar, this.eventHandler, 'fit', 'Fit model to window', false, () => { this.FitModelToWindow (false); }); AddButton (this.toolbar, this.eventHandler, 'up_y', 'Set Y axis as up vector', false, () => { this.viewer.SetUpVector (OV.Direction.Y, true); }); AddButton (this.toolbar, this.eventHandler, 'up_z', 'Set Z axis as up vector', false, () => { this.viewer.SetUpVector (OV.Direction.Z, true); }); AddButton (this.toolbar, this.eventHandler, 'flip', 'Flip up vector', false, () => { this.viewer.FlipUpVector (); }); AddSeparator (this.toolbar); AddRadioButton (this.toolbar, this.eventHandler, ['fix_up_on', 'fix_up_off'], ['Fixed up vector', 'Free orbit'], 0, false, (buttonIndex) => { if (buttonIndex === 0) { this.viewer.SetFixUpVector (true); } else if (buttonIndex === 1) { this.viewer.SetFixUpVector (false); } }); AddSeparator (this.toolbar, true); AddButton (this.toolbar, this.eventHandler, 'export', 'Export model', true, () => { let exportDialog = new OV.ExportDialog ({ onDialog : (dialog) => { this.dialog = dialog; } }); exportDialog.Show (this.model, this.viewer); }); AddButton (this.toolbar, this.eventHandler, 'share', 'Share model', true, () => { this.dialog = OV.ShowSharingDialog (importer, this.settings, this.viewer.GetCamera ()); }); // TODO: remove let theme = 'dark'; AddButton (this.toolbar, this.eventHandler, 'share', 'Dark Mode', true, () => { if (theme === 'dark') { theme = 'light' } else { theme = 'dark'; } this.themeHandler.SwitchTheme (theme); }); this.parameters.fileInput.on ('change', (ev) => { if (ev.target.files.length > 0) { this.eventHandler.HandleEvent ('model_load_started', { source : 'open_file' }); this.LoadModelFromFileList (ev.target.files); } }); } InitDragAndDrop () { window.addEventListener ('dragstart', (ev) => { ev.preventDefault (); }, false); window.addEventListener ('dragover', (ev) => { ev.stopPropagation (); ev.preventDefault (); ev.dataTransfer.dropEffect = 'copy'; }, false); window.addEventListener ('drop', (ev) => { ev.stopPropagation (); ev.preventDefault (); if (ev.dataTransfer.files.length > 0) { this.eventHandler.HandleEvent ('model_load_started', { source : 'drop' }); this.LoadModelFromFileList (ev.dataTransfer.files); } }, false); } InitModelLoader () { OV.InitModelLoader (this.modelLoader, { onStart : () => { this.ClearModel (); }, onFinish : (importResult, threeMeshes) => { this.OnModelFinished (importResult, threeMeshes); let importedExtension = OV.GetFileExtension (importResult.mainFile); this.eventHandler.HandleEvent ('model_loaded', { extension : importedExtension }); }, onRender : () => { this.viewer.Render (); }, onError : (importError) => { let reason = 'unknown'; if (importError.code === OV.ImportErrorCode.NoImportableFile) { reason = 'no_importable_file'; } else if (importError.code === OV.ImportErrorCode.ImportFailed) { reason = 'import_failed'; } let extensions = []; let importer = this.modelLoader.GetImporter (); let fileList = importer.GetFileList ().GetFiles (); for (let i = 0; i < fileList.length; i++) { extensions.push (fileList[i].extension); } this.eventHandler.HandleEvent ('model_load_failed', { reason : reason, extensions : extensions }); } }); } InitSidebar () { function AddSidebarButton (toolbar, eventHandler, sidebarPanel, onClick) { let button = toolbar.AddImageButton (sidebarPanel.image, sidebarPanel.title, () => { eventHandler.HandleEvent ('sidebar_clicked', { item : sidebarPanel.image }); onClick (); }); button.AddClass ('only_full_width'); button.AddClass ('right'); return button; } function UpdateSidebarButtons (sidebar, sidebarPanels) { const visiblePanelId = sidebar.GetVisiblePanelId (); for (let i = 0; i < sidebarPanels.length; i++) { let sidebarPanel = sidebarPanels[i]; if (sidebarPanel.panelId === visiblePanelId) { sidebarPanel.button.AddImageClass ('selected'); } else { sidebarPanel.button.RemoveImageClass ('selected'); } } } function ShowSidebar (sidebar, cookieHandler, sidebarPanels, panelId) { sidebar.Show (panelId); UpdateSidebarButtons (sidebar, sidebarPanels); cookieHandler.SetBoolVal ('ov_show_sidebar', sidebar.IsVisible ()); } function ToggleSidebar (sidebar, cookieHandler, sidebarPanels, panelId) { if (sidebar.GetVisiblePanelId () !== panelId) { ShowSidebar (sidebar, cookieHandler, sidebarPanels, panelId); } else { ShowSidebar (sidebar, cookieHandler, sidebarPanels, null); } } this.detailsPanel = new OV.DetailsSidebarPanel (this.parameters.sidebarDiv); this.settingsPanel = new OV.SettingsSidebarPanel (this.parameters.sidebarDiv); let sidebarPanels = [ { panelId : null, panel : this.detailsPanel, image : 'details', title : 'Details panel', button : null }, { panelId : null, panel : this.settingsPanel, image : 'settings', title : 'Settings panel', button : null } ]; for (let id = 0; id < sidebarPanels.length; id++) { let sidebarPanel = sidebarPanels[id]; sidebarPanel.panelId = this.sidebar.AddPanel (sidebarPanel.panel); sidebarPanel.button = AddSidebarButton (this.toolbar, this.eventHandler, sidebarPanel, () => { ToggleSidebar (this.sidebar, this.cookieHandler, sidebarPanels, sidebarPanel.panelId); this.Resize (); }); sidebarPanel.panel.Init ({ onClose : () => { ShowSidebar (this.sidebar, this.cookieHandler, sidebarPanels, null); this.Resize (); } }); } let defaultSettings = new OV.WebsiteSettings (); this.settingsPanel.InitSettings ( this.settings, defaultSettings, { onBackgroundColorChange : (newVal) => { this.settings.backgroundColor = newVal; this.settings.SaveToCookies (this.cookieHandler); this.viewer.SetBackgroundColor (newVal); }, onDefaultColorChange : (newVal) => { this.settings.defaultColor = newVal; this.settings.SaveToCookies (this.cookieHandler); if (this.modelLoader.defaultMaterial !== null) { OV.ReplaceDefaultMaterialColor (this.model, newVal); this.modelLoader.ReplaceDefaultMaterialColor (newVal); } this.viewer.Render (); } } ); let show = this.cookieHandler.GetBoolVal ('ov_show_sidebar', true); ShowSidebar (this.sidebar, this.cookieHandler, sidebarPanels, show ? sidebarPanels[0].panelId : null); } InitNavigator () { function GetMeshUserData (viewer, meshIndex) { let userData = null; viewer.EnumerateMeshesUserData ((meshUserData) => { if (meshUserData.originalMeshIndex === meshIndex) { userData = meshUserData; } }); return userData; } function GetMeshesForMaterial (viewer, model, materialIndex) { let usedByMeshes = []; viewer.EnumerateMeshesUserData ((meshUserData) => { if (meshUserData.originalMaterials.indexOf (materialIndex) !== -1) { const mesh = model.GetMesh (meshUserData.originalMeshIndex); usedByMeshes.push ({ index : meshUserData.originalMeshIndex, name : mesh.GetName () }); } }); return usedByMeshes; } function GetMaterialReferenceInfo (model, materialIndex) { const material = model.GetMaterial (materialIndex); return { index : materialIndex, name : material.name, color : material.color.Clone () }; } function GetMaterialsForMesh (viewer, model, meshIndex) { let usedMaterials = []; let userData = GetMeshUserData (viewer, meshIndex); for (let i = 0; i < userData.originalMaterials.length; i++) { const materialIndex = userData.originalMaterials[i]; usedMaterials.push (GetMaterialReferenceInfo (model, materialIndex)); } usedMaterials.sort ((a, b) => { return a.index - b.index; }); return usedMaterials; } function GetMaterialsForModel (model) { let usedMaterials = []; for (let materialIndex = 0; materialIndex < model.MaterialCount (); materialIndex++) { usedMaterials.push (GetMaterialReferenceInfo (model, materialIndex)); } return usedMaterials; } this.navigator.Init ({ openFileBrowserDialog : () => { this.OpenFileBrowserDialog (); }, updateMeshesVisibility : () => { this.UpdateMeshesVisibility (); }, updateMeshesSelection : () => { this.UpdateMeshesSelection (); }, fitMeshToWindow : (meshIndex) => { this.FitMeshToWindow (meshIndex); }, getMeshesForMaterial : (materialIndex) => { return GetMeshesForMaterial (this.viewer, this.model, materialIndex); }, getMaterialsForMesh : (meshIndex) => { return GetMaterialsForMesh (this.viewer, this.model, meshIndex); }, getMaterialsForModel : () => { return GetMaterialsForModel (this.model); }, onModelSelected : () => { this.detailsPanel.AddElementProperties (this.model); }, onMeshSelected : (meshIndex) => { this.detailsPanel.AddElementProperties (this.model.GetMesh (meshIndex)); }, onMaterialSelected : (materialIndex) => { this.detailsPanel.AddMaterialProperties (this.model.GetMaterial (materialIndex)); } }); } InitCookieConsent () { let accepted = this.cookieHandler.GetBoolVal ('ov_cookie_consent', false); if (accepted) { return; } OV.ShowCookieDialog (() => { this.cookieHandler.SetBoolVal ('ov_cookie_consent', true); }); } };