UNPKG

angular-3d-viewer

Version:
617 lines (539 loc) 18 kB
import { Coord3D, CoordDistance3D, SubCoord3D } from '../geometry/coord3d.js'; import { DegRad, Direction, IsEqual } from '../geometry/geometry.js'; import { ColorComponentToFloat } from '../model/color.js'; import { ShadingType } from '../threejs/threeutils.js'; import { Camera, CameraMode } from './camera.js'; import { GetDomElementInnerDimensions } from './domutils.js'; import { Navigation } from './navigation.js'; import { ShadingModel } from './shadingmodel.js'; import { ViewerModel, ViewerMainModel } from './viewermodel.js'; import * as THREE from 'three'; export function GetDefaultCamera (direction) { let fieldOfView = 45.0; if (direction === Direction.X) { return new Camera ( new Coord3D (2.0, -3.0, 1.5), new Coord3D (0.0, 0.0, 0.0), new Coord3D (1.0, 0.0, 0.0), fieldOfView ); } else if (direction === Direction.Y) { return new Camera ( new Coord3D (-1.5, 2.0, 3.0), new Coord3D (0.0, 0.0, 0.0), new Coord3D (0.0, 1.0, 0.0), fieldOfView ); } else if (direction === Direction.Z) { return new Camera ( new Coord3D (-1.5, -3.0, 2.0), new Coord3D (0.0, 0.0, 0.0), new Coord3D (0.0, 0.0, 1.0), fieldOfView ); } return null; } export function TraverseThreeObject (object, processor) { if (!processor (object)) { return false; } for (let child of object.children) { if (!TraverseThreeObject (child, processor)) { return false; } } return true; } export function GetShadingTypeOfObject (mainObject) { let shadingType = null; TraverseThreeObject (mainObject, (obj) => { if (obj.isMesh) { for (const material of obj.material) { if (material.type === 'MeshPhongMaterial') { shadingType = ShadingType.Phong; } else if (material.type === 'MeshStandardMaterial') { shadingType = ShadingType.Physical; } return false; } } return true; }); return shadingType; } export class CameraValidator { constructor () { this.eyeCenterDistance = 0.0; this.forceUpdate = true; } ForceUpdate () { this.forceUpdate = true; } ValidatePerspective () { if (this.forceUpdate) { this.forceUpdate = false; return false; } return true; } ValidateOrthographic (eyeCenterDistance) { if (this.forceUpdate || !IsEqual (this.eyeCenterDistance, eyeCenterDistance)) { this.eyeCenterDistance = eyeCenterDistance; this.forceUpdate = false; return false; } return true; } } export class UpVector { constructor () { this.direction = Direction.Y; this.isFixed = true; this.isFlipped = false; } SetDirection (newDirection, oldCamera) { this.direction = newDirection; this.isFlipped = false; let defaultCamera = GetDefaultCamera (this.direction); let defaultDir = SubCoord3D (defaultCamera.eye, defaultCamera.center); let distance = CoordDistance3D (oldCamera.center, oldCamera.eye); let newEye = oldCamera.center.Clone ().Offset (defaultDir, distance); let newCamera = oldCamera.Clone (); if (this.direction === Direction.X) { newCamera.up = new Coord3D (1.0, 0.0, 0.0); newCamera.eye = newEye; } else if (this.direction === Direction.Y) { newCamera.up = new Coord3D (0.0, 1.0, 0.0); newCamera.eye = newEye; } else if (this.direction === Direction.Z) { newCamera.up = new Coord3D (0.0, 0.0, 1.0); newCamera.eye = newEye; } return newCamera; } SetFixed (isFixed, oldCamera) { this.isFixed = isFixed; if (this.isFixed) { return this.SetDirection (this.direction, oldCamera); } return null; } Flip (oldCamera) { this.isFlipped = !this.isFlipped; let newCamera = oldCamera.Clone (); newCamera.up.MultiplyScalar (-1.0); return newCamera; } } export class Viewer { constructor () { THREE.ColorManagement.enabled = false; this.canvas = null; this.renderer = null; this.scene = null; this.mainModel = null; this.extraModel = null; this.camera = null; this.cameraMode = null; this.cameraValidator = null; this.shadingModel = null; this.navigation = null; this.upVector = null; this.settings = { animationSteps : 40 }; } Init (canvas) { this.canvas = canvas; this.canvas.id = 'viewer'; let parameters = { canvas : this.canvas, antialias : true }; this.renderer = new THREE.WebGLRenderer (parameters); this.renderer.outputColorSpace = THREE.LinearSRGBColorSpace; if (window.devicePixelRatio) { this.renderer.setPixelRatio (window.devicePixelRatio); } this.renderer.setClearColor ('#ffffff', 1.0); this.renderer.setSize (this.canvas.width, this.canvas.height); this.scene = new THREE.Scene (); this.mainModel = new ViewerMainModel (this.scene); this.extraModel = new ViewerModel (this.scene); this.InitNavigation (); this.InitShading (); this.Render (); } SetMouseClickHandler (onMouseClick) { this.navigation.SetMouseClickHandler (onMouseClick); } SetMouseMoveHandler (onMouseMove) { this.navigation.SetMouseMoveHandler (onMouseMove); } SetContextMenuHandler (onContext) { this.navigation.SetContextMenuHandler (onContext); } SetEdgeSettings (edgeSettings) { let newEdgeSettings = edgeSettings.Clone (); this.mainModel.SetEdgeSettings (newEdgeSettings); this.Render (); } SetEnvironmentMapSettings (environmentSettings) { let newEnvironmentSettings = environmentSettings.Clone (); this.shadingModel.SetEnvironmentMapSettings (newEnvironmentSettings, () => { this.Render (); }); this.shadingModel.UpdateShading (); this.Render (); } SetBackgroundColor (color) { let bgColor = new THREE.Color ( ColorComponentToFloat (color.r), ColorComponentToFloat (color.g), ColorComponentToFloat (color.b) ); let alpha = ColorComponentToFloat (color.a); this.renderer.setClearColor (bgColor, alpha); this.Render (); } GetCanvas () { return this.canvas; } GetCamera () { return this.navigation.GetCamera (); } GetCameraMode () { return this.cameraMode; } SetCamera (camera) { this.navigation.SetCamera (camera); this.cameraValidator.ForceUpdate (); this.Render (); } SetCameraMode (cameraMode) { if (this.cameraMode === cameraMode) { return; } this.scene.remove (this.camera); if (cameraMode === CameraMode.Perspective) { this.camera = new THREE.PerspectiveCamera (45.0, 1.0, 0.1, 1000.0); } else if (cameraMode === CameraMode.Orthographic) { this.camera = new THREE.OrthographicCamera (-1.0, 1.0, 1.0, -1.0, 0.1, 1000.0); } this.scene.add (this.camera); this.cameraMode = cameraMode; this.shadingModel.SetCameraMode (cameraMode); this.cameraValidator.ForceUpdate (); this.AdjustClippingPlanes (); this.Render (); } Resize (width, height) { let innerSize = GetDomElementInnerDimensions (this.canvas, width, height); this.ResizeRenderer (innerSize.width, innerSize.height); } ResizeRenderer (width, height) { if (window.devicePixelRatio) { this.renderer.setPixelRatio (window.devicePixelRatio); } this.renderer.setSize (width, height); this.cameraValidator.ForceUpdate (); this.Render (); } FitSphereToWindow (boundingSphere, animation) { if (boundingSphere === null) { return; } let center = new Coord3D (boundingSphere.center.x, boundingSphere.center.y, boundingSphere.center.z); let radius = boundingSphere.radius; let newCamera = this.navigation.GetFitToSphereCamera (center, radius); this.navigation.MoveCamera (newCamera, animation ? this.settings.animationSteps : 0); } AdjustClippingPlanes () { let boundingSphere = this.GetBoundingSphere ((meshUserData) => { return true; }); this.AdjustClippingPlanesToSphere (boundingSphere); } AdjustClippingPlanesToSphere (boundingSphere) { if (boundingSphere === null) { return; } if (boundingSphere.radius < 10.0) { this.camera.near = 0.01; this.camera.far = 100.0; } else if (boundingSphere.radius < 100.0) { this.camera.near = 0.1; this.camera.far = 1000.0; } else if (boundingSphere.radius < 1000.0) { this.camera.near = 10.0; this.camera.far = 10000.0; } else { this.camera.near = 100.0; this.camera.far = 1000000.0; } this.cameraValidator.ForceUpdate (); this.Render (); } IsFixUpVector () { return this.navigation.IsFixUpVector (); } SetFixUpVector (isFixUpVector) { let oldCamera = this.navigation.GetCamera (); let newCamera = this.upVector.SetFixed (isFixUpVector, oldCamera); this.navigation.SetFixUpVector (isFixUpVector); if (newCamera !== null) { this.navigation.MoveCamera (newCamera, this.settings.animationSteps); } this.Render (); } SetUpVector (upDirection, animate) { let oldCamera = this.navigation.GetCamera (); let newCamera = this.upVector.SetDirection (upDirection, oldCamera); let animationSteps = animate ? this.settings.animationSteps : 0; this.navigation.MoveCamera (newCamera, animationSteps); this.Render (); } FlipUpVector () { let oldCamera = this.navigation.GetCamera (); let newCamera = this.upVector.Flip (oldCamera); this.navigation.MoveCamera (newCamera, 0); this.Render (); } Render () { let navigationCamera = this.navigation.GetCamera (); this.camera.position.set (navigationCamera.eye.x, navigationCamera.eye.y, navigationCamera.eye.z); this.camera.up.set (navigationCamera.up.x, navigationCamera.up.y, navigationCamera.up.z); this.camera.lookAt (new THREE.Vector3 (navigationCamera.center.x, navigationCamera.center.y, navigationCamera.center.z)); if (this.cameraMode === CameraMode.Perspective) { if (!this.cameraValidator.ValidatePerspective ()) { this.camera.aspect = this.canvas.width / this.canvas.height; this.camera.fov = navigationCamera.fov; this.camera.updateProjectionMatrix (); } } else if (this.cameraMode === CameraMode.Orthographic) { let eyeCenterDistance = CoordDistance3D (navigationCamera.eye, navigationCamera.center); if (!this.cameraValidator.ValidateOrthographic (eyeCenterDistance)) { let aspect = this.canvas.width / this.canvas.height; let eyeCenterDistance = CoordDistance3D (navigationCamera.eye, navigationCamera.center); let frustumHalfHeight = eyeCenterDistance * Math.tan (0.5 * navigationCamera.fov * DegRad); this.camera.left = -frustumHalfHeight * aspect; this.camera.right = frustumHalfHeight * aspect; this.camera.top = frustumHalfHeight; this.camera.bottom = -frustumHalfHeight; this.camera.updateProjectionMatrix (); } } this.shadingModel.UpdateByCamera (navigationCamera); this.renderer.render (this.scene, this.camera); } SetMainObject (object) { const shadingType = GetShadingTypeOfObject (object); this.mainModel.SetMainObject (object); this.shadingModel.SetShadingType (shadingType); this.Render (); } AddExtraObject (object) { this.extraModel.AddObject (object); this.Render (); } Clear () { this.mainModel.Clear (); this.extraModel.Clear (); this.Render (); } ClearExtra () { this.extraModel.Clear (); this.Render (); } SetMeshesVisibility (isVisible) { this.mainModel.EnumerateMeshes ((mesh) => { let visible = isVisible (mesh.userData); if (mesh.visible !== visible) { mesh.visible = visible; } }); this.mainModel.EnumerateEdges ((edge) => { let visible = isVisible (edge.userData); if (edge.visible !== visible) { edge.visible = visible; } }); this.Render (); } SetMeshesHighlight (highlightColor, isHighlighted) { function CreateHighlightMaterials (originalMaterials, highlightMaterial) { let highlightMaterials = []; for (let i = 0; i < originalMaterials.length; i++) { highlightMaterials.push (highlightMaterial); } return highlightMaterials; } const highlightMaterial = this.CreateHighlightMaterial (highlightColor); this.mainModel.EnumerateMeshes ((mesh) => { let highlighted = isHighlighted (mesh.userData); if (highlighted) { if (mesh.userData.threeMaterials === null) { mesh.userData.threeMaterials = mesh.material; mesh.material = CreateHighlightMaterials (mesh.material, highlightMaterial); } } else { if (mesh.userData.threeMaterials !== null) { mesh.material = mesh.userData.threeMaterials; mesh.userData.threeMaterials = null; } } }); this.Render (); } CreateHighlightMaterial (highlightColor) { const showEdges = this.mainModel.edgeSettings.showEdges; return this.shadingModel.CreateHighlightMaterial (highlightColor, showEdges); } GetMeshUserDataUnderMouse (mouseCoords) { let intersection = this.GetMeshIntersectionUnderMouse (mouseCoords); if (intersection === null) { return null; } return intersection.object.userData; } GetMeshIntersectionUnderMouse (mouseCoords) { let canvasSize = this.GetCanvasSize (); let intersection = this.mainModel.GetMeshIntersectionUnderMouse (mouseCoords, this.camera, canvasSize.width, canvasSize.height); if (intersection === null) { return null; } return intersection; } GetBoundingBox (needToProcess) { return this.mainModel.GetBoundingBox (needToProcess); } GetBoundingSphere (needToProcess) { return this.mainModel.GetBoundingSphere (needToProcess); } EnumerateMeshesUserData (enumerator) { this.mainModel.EnumerateMeshes ((mesh) => { enumerator (mesh.userData); }); } InitNavigation () { let camera = GetDefaultCamera (Direction.Y); this.camera = new THREE.PerspectiveCamera (45.0, 1.0, 0.1, 1000.0); this.cameraMode = CameraMode.Perspective; this.cameraValidator = new CameraValidator (); this.scene.add (this.camera); let canvasElem = this.renderer.domElement; this.navigation = new Navigation (canvasElem, camera, { onUpdate : () => { this.Render (); } }); this.upVector = new UpVector (); } InitShading () { this.shadingModel = new ShadingModel (this.scene); } GetShadingType () { return this.shadingModel.type; } GetImageSize () { let originalSize = new THREE.Vector2 (); this.renderer.getSize (originalSize); return { width : parseInt (originalSize.x, 10), height : parseInt (originalSize.y, 10) }; } GetCanvasSize () { let width = this.canvas.width; let height = this.canvas.height; if (window.devicePixelRatio) { width /= window.devicePixelRatio; height /= window.devicePixelRatio; } return { width : width, height : height }; } GetImageAsDataUrl (width, height, isTransparent) { let originalSize = this.GetImageSize (); let renderWidth = width; let renderHeight = height; if (window.devicePixelRatio) { renderWidth /= window.devicePixelRatio; renderHeight /= window.devicePixelRatio; } let clearAlpha = this.renderer.getClearAlpha (); if (isTransparent) { this.renderer.setClearAlpha (0.0); } this.ResizeRenderer (renderWidth, renderHeight); this.Render (); let url = this.renderer.domElement.toDataURL (); this.ResizeRenderer (originalSize.width, originalSize.height); this.renderer.setClearAlpha (clearAlpha); return url; } Destroy () { this.Clear (); this.renderer.dispose (); } }