const ZA = -1; const LIGHT_RATIO = 32 ; class module_D3 { canvas = null; $Scene = null; $Camera = null; $Renderer = null; $Root = null; camera_target = new THREE.Vector3(0,0,0); camera_euler = new THREE.Euler(0,0,0,'YXZ'); MATERIAL = {} ; LIGHT = {} ; axis_helper = null ; Dispose() { this.$Renderer?.clear(true, true, true); for( let id in this.LIGHT ) { this.$Root.remove(this.LIGHT[id]); } this.LIGHT = {}; THREE.Cache.clear(); this.$Renderer.dispose(); this.$Renderer.domElement.remove(); this.canvas = null; } constructor() { var $aspect = µB.$Config.width / µB.$Config.height; this.$Root = new THREE.Group(); this.$Scene = new THREE.Scene(); this.$Scene.add( this.$Root ); this.$Camera = new THREE.PerspectiveCamera( 70, $aspect, 0.1, 1000 ); this.$Camera.position.set(0,0,-50*ZA); this.$Camera.lookAt(0,0,0); this.$Renderer = new THREE.WebGLRenderer( { antialias:true, alpha:true, stencil:false, preserveDrawingBuffer:true} ); this.$Renderer.setPixelRatio( 1 ) ; this.$Renderer.setClearColor( 0x000000, 0); this.$Renderer.setSize(µB.$Config.width,µB.$Config.height,false); THREE.ColorManagement.enabled = true; this.$Renderer.outputColorSpace = THREE.LinearSRGBColorSpace ; this.$Renderer.shadowMap.enabled = true; this.$Renderer.shadowMap.type = THREE.PCFSoftShadowMap; Log( "Canvas/D3" ); this.$Renderer.domElement.className = "d3" ; General.AppendChild(this.$Renderer.domElement); this.canvas = this.$Renderer.domElement ; this.canvas.style.zIndex = Z_D3 ; this.PrepareScene() ; this.PrepareMaterials() ; } PrepareScene() { this.create_light( "glow" ) ; this.light_power( "glow", 25 ) ; this.create_light( "sun" ) ; this.move_light( "sun", -5,5,-5 ) ; this.light_power( "sun", 100 ) ; } PrepareMaterials() { let DEF = [ { type:1, id:"normal", roughness:1.00, metalness:0.00 }, { type:1, id:"metal", roughness:0.30, metalness:0.40 }, { type:1, id:"glass", roughness:0.05, metalness:0.00, transparent:true }, { type:2, id:"neon", roughness:1.00, metalness:0.00, transparent:true, emissive:0x888888, blending:THREE.AdditiveBlending, lights:false }, { type:2, id:"ui", roughness:1.00, metalness:0.00, transparent:true, emissive:0x888888, fog:false, blending:THREE.AdditiveBlending, lights:false, depthTest:false, depthWrite:false } ]; for( let PAINT of DEF ) { let T = { color: 0xffffff, vertexColors: true, side: PAINT.side??THREE.FrontSide, transparent: PAINT.transparent??false, blending: PAINT.blending??THREE.NormalBlending, depthWrite: PAINT.depthWrite??true, depthTest: PAINT.depthTest??true, fog: PAINT.fog??true, dithering: true }; if( PAINT.type === 1 ) { T.roughness = PAINT.roughness; T.metalness = PAINT.metalness; T.flatShading = true; T.emissive = PAINT.emissive??0x0 ; this.MATERIAL[PAINT.id] = new THREE.MeshStandardMaterial(T) ; } else if( PAINT.type === 2 ) { T.premultipliedAlpha = true; this.MATERIAL[PAINT.id] = new THREE.MeshBasicMaterial(T) ; } } } Update() { if( µB.$Config.is_autobuild ) { µB.$Sprite.UpdateDirty(); } this.$Renderer?.render( this.$Scene, this.$Camera ); } Resize(x, y) { let aspect = x/y ; this.$Camera.aspect = x/y ; this.$Camera.updateProjectionMatrix(); this.$Renderer.setSize(x,y,false); } IsLight( name, silent=false ) { if( ! this.LIGHT[name] ) { if( ! silent ) { µB.RuntimeError( "Light " + name + " doesn't exist" ) ; } return false ; } else { return true ; } } show_axis() { if( this.axis_helper == null ) { this.axis_helper = new THREE.AxesHelper(3); this.axis_helper.scale.set(8,8,8*ZA); this.$Root.add(this.axis_helper); } } hide_axis() { if( this.axis_helper != null ) { this.$Root.remove(this.axis_helper); this.axis_helper = null ; } } free_camera() { this.camera_target = null; } move_camera_all($x) { this.camera_move($x,$x,$x*ZA) ; } move_camera($x, $y, $z) { this.$Camera.position.set($x,$y,$z*ZA); if( this.camera_target ) { this.$Camera.lookAt(this.camera_target); } } move_camera_by_all($x) { this.camera_move_by($x,$x,$x*ZA); } move_camera_by($x, $y, $z) { this.$Camera.position.x += $x ; this.$Camera.position.y += $y ; this.$Camera.position.z += $z*ZA ; if( this.camera_target ) { this.$Camera.lookAt(this.camera_target); } } camera_position_set($xyz, $value) { if( $xyz === "z" ) $value = $value*ZA ; this.$Camera.position[$xyz] = $value ; if( this.camera_target ) { this.$Camera.lookAt(this.camera_target); } } target_camera( $x,$y,$z ) { this.camera_target = new THREE.Vector3($x,$y,$z*ZA); this.$Camera.lookAt(this.camera_target); } turn_camera(x, y, z) { this.camera_euler.x = x * µB.$Config.deg ; this.camera_euler.y = y * µB.$Config.deg ; this.camera_euler.z = z * µB.$Config.deg ; this.$Camera.setRotationFromEuler( this.camera_euler ) ; } turn_camera_by(x, y, z) { this.camera_euler.x += x * µB.$Config.deg ; this.camera_euler.y += y * µB.$Config.deg ; this.camera_euler.z += z * µB.$Config.deg ; this.$Camera.setRotationFromEuler( this.camera_euler ) ; } hide_fog() { this.$Scene.fog = null ; this.$Scene.background = null ; } show_fog( $color, $distance ) { if( $color == 0 ) { this.$Scene_fog_off(); } else { let $rgb = µB.$Config.Color2Hex24($color) ; this.$Scene.fog = new THREE.Fog($rgb, 1, $distance ); this.$Scene.background = new THREE.Color($rgb); } } fov( v ) { this.$Camera.fov = v*1 ; this.$Camera.updateProjectionMatrix(); } delete_light($name) { if( this.IsLight($name) ) { if( this.LIGHT[$name].node.target ) { this.$Root.remove(this.LIGHT[$name].node.target); } this.$Root.remove(this.LIGHT[$name].node); delete this.LIGHT[$name] ; } } create_light(type) { this.create_light_as(type, type); } create_light_as(type, id) { type = µB.$Config.GetEnum(type); if( this.IsLight(id,true) ) { this.delete_light(id); } let light = null; let multiply = 1 ; if( type == "glow" ) { light = new THREE.AmbientLight(0xFFFFFF,1); } else if( type == "orb" ) { light = new THREE.PointLight(0xFFFFFF,1,0,1); multiply = 10 ; } else if( type == "sun" ) { light = new THREE.DirectionalLight(0xFFFFFF,1); } else if( type == "shadow" ) { light = new THREE.DirectionalLight(0xFFFFFF,1); light.castShadow = true ; /* $light.shadow.camera.near=1; $light.shadow.camera.far=64; $light.shadow.bias= -0.05; $light.shadow.camera.left = -50; $light.shadow.camera.bottom = -50; $light.shadow.camera.top = 50; $light.shadow.camera.right = 50; */ } else { Guru( "? Light type: "+type ) ; } if( light ) { this.LIGHT[id] = new Light(light,multiply); this.$Root.add(light); if( type >= 10 ) { this.$Root.add(light.target); } this.light_power(id, 100); } } show_light(id) { if( this.IsLight(id) ) { this.LIGHT[id].node.visible = true ; } } hide_light(id) { if( this.IsLight(id) ) { this.LIGHT[id].node.visible = false ; } } light_power(id, power) { if( this.IsLight(id) ) { this.LIGHT[id].node.visible = true ; this.LIGHT[id].node.intensity = this.LIGHT[id].multiply * power / LIGHT_RATIO ; } } light_color(id, color) { if( this.IsLight(id) ) { let rgb = µB.$Config.Color2Hex24( color ); this.LIGHT[id].node.visible = true ; this.LIGHT[id].node.color.set( rgb ) ; } } target_light(id, x, y, z) { if( this.IsLight(id) ) { if( this.LIGHT[id].node.target ) { this.LIGHT[id].node.target.position.set(x,y,z*ZA) ; } } } move_light_all(id, a) { this.light_move(id,a,a,a*ZA); } move_light(id, x, y, z) { if( this.IsLight(id) ) { this.LIGHT[id].node.position.set(x,y,z*ZA) ; } } move_light_by(id, x, y, z) { if( this.IsLight(id) ) { this.LIGHT[id].node.position.x += x; this.LIGHT[id].node.position.y += y; this.LIGHT[id].node.position.z += z*ZA; } } light_position_set(id, xyz, $value) { if( xyz === "z" ) value = value*ZA ; if( this.IsLight(id) ) { this.LIGHT[id].node.position[xyz] = value ; } } };