diff --git a/config/webpack.common.ts b/config/webpack.common.ts index f4753d6a5..0eae8241e 100644 --- a/config/webpack.common.ts +++ b/config/webpack.common.ts @@ -21,16 +21,6 @@ const config: webpack.Configuration = { resolve: { alias: { "dat.gui": getpath('../node_modules/dat.gui/build/dat.gui.js'), - "three-FBXLoader": getpath("../src/Loader/FBXLoader.js"), - "three-CopyShader": getpath("../node_modules/three/examples/js/shaders/CopyShader.js"), - "three-SMAAShader": getpath("../node_modules/three/examples/js/shaders/SMAAShader.js"), - "three-FXAAShader": getpath("../node_modules/three/examples/js/shaders/FXAAShader.js"), - - "three-OutlinePass": getpath("../src/GraphicsSystem/OutlinePass.js"), - "three-EffectComposer": getpath("../node_modules/three/examples/js/postprocessing/EffectComposer.js"), - "three-RenderPass": getpath("../node_modules/three/examples/js/postprocessing/RenderPass.js"), - "three-ShaderPass": getpath("../node_modules/three/examples/js/postprocessing/ShaderPass.js"), - "three-SMAAPass": getpath("../node_modules/three/examples/js/postprocessing/SMAAPass.js"), }, extensions: [".ts", ".tsx", ".js", "json"] }, diff --git a/package.json b/package.json index a01807cbd..573bb7887 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@types/react-dom": "^16.8.4", "@types/stats.js": "^0.17.0", "@types/terser-webpack-plugin": "^1.2.1", - "@types/three": "^0.93.15", + "@types/three": "^0.103.2", "@types/webpack": "^4.4.34", "@types/webpack-dev-server": "^3.1.6", "@types/webpack-env": "^1.13.7", @@ -79,7 +79,7 @@ "dxf-parser": "^1.0.0-alpha.1", "expr-eval": "^1.2.2", "golden-layout": "^1.5.9", - "meriyah": "^0.6.0", + "meriyah": "^0.6.1", "mobx": "^5.10.1", "mobx-react": "^6.1.1", "pako": "^1.0.10", @@ -88,7 +88,7 @@ "react-dom": "^16.8.2", "react-rnd2": "^1.0.1", "stats.js": "^0.17.0", - "three": "^0.101.1", + "three": "^0.105.2", "xaop": "^1.3.2" }, "jest": { diff --git a/src/Add-on/loadfbx.ts b/src/Add-on/loadfbx.ts index 3c89e2ca3..24ea43635 100644 --- a/src/Add-on/loadfbx.ts +++ b/src/Add-on/loadfbx.ts @@ -1,7 +1,8 @@ -import { Command } from "../Editor/CommandMachine"; -import { FileSystem } from "../Common/FileSystem"; +import { LoadingManager } from "three"; +import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader"; import { app } from '../ApplicationServices/Application'; -import { LoadingManager, FBXLoader } from "three"; +import { FileSystem } from "../Common/FileSystem"; +import { Command } from "../Editor/CommandMachine"; export class Fbx implements Command { diff --git a/src/Common/CurveUtils.ts b/src/Common/CurveUtils.ts index 80d3524e9..bcffd2fdb 100644 --- a/src/Common/CurveUtils.ts +++ b/src/Common/CurveUtils.ts @@ -503,7 +503,6 @@ export function CircleOuterTangentLines(circle0: Circle, circle1: Circle): Line[ export function getArcOrCirNearPts(cu: Circle | Arc | Ellipse, pickPoint: Vector3, viewXform: Matrix3) { - //@ts-ignore let viewNormal = new Vector3().fromArray(viewXform.elements, 2 * 3); let plane = new PlaneExt(cu.Normal, cu.Center); @@ -512,9 +511,7 @@ export function getArcOrCirNearPts(cu: Circle | Arc | Ellipse, pickPoint: Vector if (pickLocal) { - //@ts-ignore let x = new Vector3().fromArray(viewXform.elements, 0).add(pickLocal); - //@ts-ignore let y = new Vector3().fromArray(viewXform.elements, 3).add(pickLocal); x = plane.intersectLine(new Line3(x, x.clone().add(viewNormal)), new Vector3(), true); diff --git a/src/DatabaseServices/Entity/Entity.ts b/src/DatabaseServices/Entity/Entity.ts index e9505eb79..6dbdae5d5 100644 --- a/src/DatabaseServices/Entity/Entity.ts +++ b/src/DatabaseServices/Entity/Entity.ts @@ -227,21 +227,37 @@ export class Entity extends CADObject this.UpdateVisible(); let isJigIng = this._CacheDrawObject.has(RenderType.Jig); - for (let [type, en] of this._CacheDrawObject) + for (let [type, obj] of this._CacheDrawObject) { if (isJigIng && type !== RenderType.Jig) continue; if (mode & UpdateDraw.Geometry) - this.UpdateDrawObject(type, en); + { + if (obj.userData.IsClone) + { + let parent = obj.parent; + DisposeThreeObj(obj); + this._CacheDrawObject.delete(type); + let newObj = this.GetDrawObjectFromRenderType(type); + if (parent) + { + parent.remove(obj); + parent.add(newObj); + } + obj = newObj; + } + else + this.UpdateDrawObject(type, obj); + } if (mode & UpdateDraw.Material) - this.UpdateDrawObjectMaterial(type, en); + this.UpdateDrawObjectMaterial(type, obj); if (mode & UpdateDraw.Matrix || mode & UpdateDraw.Geometry) { - en.updateMatrixWorld(true); - en.traverse(UpdateBoundingSphere); + obj.updateMatrixWorld(true); + obj.traverse(UpdateBoundingSphere); } } @@ -429,16 +445,14 @@ export class Entity extends CADObject { for (let [type, obj] of from._CacheDrawObject) { - let oldD = obj.userData; - obj.traverse(o => - { - o.userData = {}; - }); + let oldUserDaata = obj.userData; + obj.traverse(o => o.userData = {}); let newObj = obj.clone(); - obj.userData = oldD; + obj.userData = oldUserDaata; newObj.matrix = this.m_Matrix; newObj.userData = { Entity: this }; + newObj.userData.IsClone = true; this._CacheDrawObject.set(type, newObj); } this.NeedUpdateFlag = UpdateDraw.None; diff --git a/src/DatabaseServices/Entity/Line.ts b/src/DatabaseServices/Entity/Line.ts index c6cbed9e4..4ad5c856c 100644 --- a/src/DatabaseServices/Entity/Line.ts +++ b/src/DatabaseServices/Entity/Line.ts @@ -84,7 +84,6 @@ export class Line extends Curve case ObjectSnapMode.Nea: { let derv = this.GetFistDeriv(0).normalize(); - //@ts-ignore let viewNormal = new Vector3().fromArray(viewXform.elements, 2 * 3); //平行不捕捉 diff --git a/src/Editor/CameraControls.ts b/src/Editor/CameraControls.ts index 6e7f96264..16a13867b 100644 --- a/src/Editor/CameraControls.ts +++ b/src/Editor/CameraControls.ts @@ -49,7 +49,6 @@ export class CameraControls this.m_domElement.addEventListener("mousemove", this.onMouseMove, false); document.addEventListener("mousemove", e => { - //@ts-ignore if (document.pointerLockElement === this.m_Viewer.Renderer.domElement) this.onMouseMove(e); }); @@ -154,12 +153,10 @@ export class CameraControls requestPointerLock() { - //@ts-ignore if (this.m_Viewer.Renderer.domElement.requestPointerLock && this.m_IsAltDown ) { - //@ts-ignore this.m_Viewer.Renderer.domElement.requestPointerLock(); this.m_Viewer.PreViewer.Cursor.CursorObject.visible = false; @@ -170,10 +167,8 @@ export class CameraControls } exitPointerLock() { - //@ts-ignore if (document.exitPointerLock) { - //@ts-ignore document.exitPointerLock(); this.m_Viewer.PreViewer.Cursor.CursorObject.visible = true; diff --git a/src/Editor/MaterialEditor.ts b/src/Editor/MaterialEditor.ts index 086549ed7..f87c2467d 100644 --- a/src/Editor/MaterialEditor.ts +++ b/src/Editor/MaterialEditor.ts @@ -1,12 +1,12 @@ import { autorun, observable } from 'mobx'; +import { AmbientLight, BoxBufferGeometry, BufferGeometry, ConeBufferGeometry, Geometry, Mesh, MeshPhysicalMaterial, Object3D, PointLight, SphereBufferGeometry, TorusBufferGeometry, TorusKnotBufferGeometry } from 'three'; import { end } from 'xaop'; +import { Singleton } from '../Common/Singleton'; +import { PhysicalMaterialRecord } from '../DatabaseServices/PhysicalMaterialRecord'; +import { TextureTableRecord } from '../DatabaseServices/Texture'; import { CameraType } from '../GraphicsSystem/CameraUpdate'; import { Viewer } from '../GraphicsSystem/Viewer'; import { MaterialEditorCamerControl } from './MaterialMouseControl'; -import { PhysicalMaterialRecord } from '../DatabaseServices/PhysicalMaterialRecord'; -import { Singleton } from '../Common/Singleton'; -import { Geometry, BufferGeometry, Object3D, Mesh, SphereBufferGeometry, TorusBufferGeometry, BoxBufferGeometry, TorusKnotBufferGeometry, ConeBufferGeometry, AmbientLight, PointLight, MeshPhysicalMaterial } from 'three'; -import { TextureTableRecord } from '../DatabaseServices/Texture'; /** * 材质编辑器 @@ -29,7 +29,7 @@ export class MaterialEditor extends Singleton initGeometrys() { - this.Geometrys = new Map( + this.Geometrys = new Map( [ ["球", new SphereBufferGeometry(1, 32, 32)], ["圆环", new TorusBufferGeometry(0.8, 0.4, 32, 64)], diff --git a/src/GraphicsSystem/FXAAPass.ts b/src/GraphicsSystem/FXAAPass.ts index 706c14897..dcf0da476 100644 --- a/src/GraphicsSystem/FXAAPass.ts +++ b/src/GraphicsSystem/FXAAPass.ts @@ -1,7 +1,5 @@ -import { FXAAShader, ShaderPass } from "three"; -require("three-EffectComposer"); -require("three-ShaderPass"); -require("three-FXAAShader"); +import { FXAAShader } from "three/examples/jsm/shaders/FXAAShader"; +import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass"; export class FXAAPass extends ShaderPass { diff --git a/src/GraphicsSystem/OutlinePass.js b/src/GraphicsSystem/OutlinePass.js deleted file mode 100644 index 87f124ff5..000000000 --- a/src/GraphicsSystem/OutlinePass.js +++ /dev/null @@ -1,581 +0,0 @@ -/** - * @author spidersharma / http://eduperiment.com/ - */ - -function isShowObject ( object ) { - return object.isMesh || object.isLine || object.isSprite; -} - -THREE.OutlinePass = function ( resolution, scene, camera, selectedObjects ) { - - this.renderObjects = []; - - this.renderScene = scene; - this.renderCamera = camera; - this.selectedObjects = selectedObjects !== undefined ? selectedObjects : []; - this.visibleEdgeColor = new THREE.Color( 1, 1, 1 ); - this.hiddenEdgeColor = new THREE.Color( 0.1, 0.04, 0.02 ); - this.edgeGlow = 0.0; - this.usePatternTexture = false; - this.edgeThickness = 1.0; - this.edgeStrength = 3.0; - this.downSampleRatio = 2; - this.pulsePeriod = 0; - - THREE.Pass.call( this ); - - this.resolution = ( resolution !== undefined ) ? new THREE.Vector2( resolution.x, resolution.y ) : new THREE.Vector2( 256, 256 ); - - var pars = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat }; - - var resx = Math.round( this.resolution.x / this.downSampleRatio ); - var resy = Math.round( this.resolution.y / this.downSampleRatio ); - - this.maskBufferMaterial = new THREE.MeshBasicMaterial( { color: 0xffffff } ); - this.maskBufferMaterial.side = THREE.DoubleSide; - this.renderTargetMaskBuffer = new THREE.WebGLRenderTarget( this.resolution.x, this.resolution.y, pars ); - this.renderTargetMaskBuffer.texture.name = "OutlinePass.mask"; - this.renderTargetMaskBuffer.texture.generateMipmaps = false; - - this.depthMaterial = new THREE.MeshDepthMaterial(); - this.depthMaterial.side = THREE.DoubleSide; - this.depthMaterial.depthPacking = THREE.RGBADepthPacking; - this.depthMaterial.blending = THREE.NoBlending; - - this.prepareMaskMaterial = this.getPrepareMaskMaterial(); - this.prepareMaskMaterial.side = THREE.DoubleSide; - this.prepareMaskMaterial.fragmentShader = replaceDepthToViewZ( this.prepareMaskMaterial.fragmentShader, this.renderCamera ); - - this.renderTargetDepthBuffer = new THREE.WebGLRenderTarget( this.resolution.x, this.resolution.y, pars ); - this.renderTargetDepthBuffer.texture.name = "OutlinePass.depth"; - this.renderTargetDepthBuffer.texture.generateMipmaps = false; - - this.renderTargetMaskDownSampleBuffer = new THREE.WebGLRenderTarget( resx, resy, pars ); - this.renderTargetMaskDownSampleBuffer.texture.name = "OutlinePass.depthDownSample"; - this.renderTargetMaskDownSampleBuffer.texture.generateMipmaps = false; - - this.renderTargetBlurBuffer1 = new THREE.WebGLRenderTarget( resx, resy, pars ); - this.renderTargetBlurBuffer1.texture.name = "OutlinePass.blur1"; - this.renderTargetBlurBuffer1.texture.generateMipmaps = false; - this.renderTargetBlurBuffer2 = new THREE.WebGLRenderTarget( Math.round( resx / 2 ), Math.round( resy / 2 ), pars ); - this.renderTargetBlurBuffer2.texture.name = "OutlinePass.blur2"; - this.renderTargetBlurBuffer2.texture.generateMipmaps = false; - - this.edgeDetectionMaterial = this.getEdgeDetectionMaterial(); - this.renderTargetEdgeBuffer1 = new THREE.WebGLRenderTarget( resx, resy, pars ); - this.renderTargetEdgeBuffer1.texture.name = "OutlinePass.edge1"; - this.renderTargetEdgeBuffer1.texture.generateMipmaps = false; - this.renderTargetEdgeBuffer2 = new THREE.WebGLRenderTarget( Math.round( resx / 2 ), Math.round( resy / 2 ), pars ); - this.renderTargetEdgeBuffer2.texture.name = "OutlinePass.edge2"; - this.renderTargetEdgeBuffer2.texture.generateMipmaps = false; - - var MAX_EDGE_THICKNESS = 4; - var MAX_EDGE_GLOW = 4; - - this.separableBlurMaterial1 = this.getSeperableBlurMaterial( MAX_EDGE_THICKNESS ); - this.separableBlurMaterial1.uniforms[ "texSize" ].value = new THREE.Vector2( resx, resy ); - this.separableBlurMaterial1.uniforms[ "kernelRadius" ].value = 1; - this.separableBlurMaterial2 = this.getSeperableBlurMaterial( MAX_EDGE_GLOW ); - this.separableBlurMaterial2.uniforms[ "texSize" ].value = new THREE.Vector2( Math.round( resx / 2 ), Math.round( resy / 2 ) ); - this.separableBlurMaterial2.uniforms[ "kernelRadius" ].value = MAX_EDGE_GLOW; - - // Overlay material - this.overlayMaterial = this.getOverlayMaterial(); - - // copy material - if ( THREE.CopyShader === undefined ) - console.error( "THREE.OutlinePass relies on THREE.CopyShader" ); - - var copyShader = THREE.CopyShader; - - this.copyUniforms = THREE.UniformsUtils.clone( copyShader.uniforms ); - this.copyUniforms[ "opacity" ].value = 1.0; - - this.materialCopy = new THREE.ShaderMaterial( { - uniforms: this.copyUniforms, - vertexShader: copyShader.vertexShader, - fragmentShader: copyShader.fragmentShader, - blending: THREE.NoBlending, - depthTest: false, - depthWrite: false, - transparent: true - } ); - - this.enabled = true; - this.needsSwap = false; - - this.oldClearColor = new THREE.Color(); - this.oldClearAlpha = 1; - - this.camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); - this.scene = new THREE.Scene(); - - this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null ); - this.quad.frustumCulled = false; // Avoid getting clipped - this.scene.add( this.quad ); - - this.tempPulseColor1 = new THREE.Color(); - this.tempPulseColor2 = new THREE.Color(); - this.textureMatrix = new THREE.Matrix4(); - - function replaceDepthToViewZ( string, camera ) { - - var type = camera.isPerspectiveCamera ? 'perspective' : 'orthographic'; - - return string.replace( /DEPTH_TO_VIEW_Z/g, type + 'DepthToViewZ' ); - - } - -}; - -THREE.OutlinePass.prototype = Object.assign( Object.create( THREE.Pass.prototype ), { - - constructor: THREE.OutlinePass, - - dispose: function () { - - this.renderTargetMaskBuffer.dispose(); - this.renderTargetDepthBuffer.dispose(); - this.renderTargetMaskDownSampleBuffer.dispose(); - this.renderTargetBlurBuffer1.dispose(); - this.renderTargetBlurBuffer2.dispose(); - this.renderTargetEdgeBuffer1.dispose(); - this.renderTargetEdgeBuffer2.dispose(); - - }, - - setSize: function ( width, height ) { - - this.renderTargetMaskBuffer.setSize( width, height ); - - var resx = Math.round( width / this.downSampleRatio ); - var resy = Math.round( height / this.downSampleRatio ); - this.renderTargetMaskDownSampleBuffer.setSize( resx, resy ); - this.renderTargetBlurBuffer1.setSize( resx, resy ); - this.renderTargetEdgeBuffer1.setSize( resx, resy ); - this.separableBlurMaterial1.uniforms[ "texSize" ].value = new THREE.Vector2( resx, resy ); - - resx = Math.round( resx / 2 ); - resy = Math.round( resy / 2 ); - - this.renderTargetBlurBuffer2.setSize( resx, resy ); - this.renderTargetEdgeBuffer2.setSize( resx, resy ); - - this.separableBlurMaterial2.uniforms[ "texSize" ].value = new THREE.Vector2( resx, resy ); - - }, - - - changeVisibilityOfSelectedObjects: function ( bVisible ) { - - function gatherSelectedMeshesCallBack( object ) { - - if ( isShowObject( object ) ) { - - if ( bVisible ) { - - object.visible = object.userData.oldVisible; - delete object.userData.oldVisible; - - } else { - - object.userData.oldVisible = object.visible; - object.visible = bVisible; - - } - - } - - } - - for ( var i = 0; i < this.selectedObjects.length; i ++ ) { - - var selectedObject = this.selectedObjects[ i ]; - selectedObject.traverse( gatherSelectedMeshesCallBack ); - - } - - }, - - changeVisibilityOfNonSelectedObjects: function ( bVisible ) { - - var selectedMeshes = []; - - function gatherSelectedMeshesCallBack( object ) { - - if ( isShowObject( object ) ) selectedMeshes.push( object ); - - } - - for ( var i = 0; i < this.selectedObjects.length; i ++ ) { - - var selectedObject = this.selectedObjects[ i ]; - selectedObject.traverse( gatherSelectedMeshesCallBack ); - - } - - function VisibilityChangeCallBack( object ) { - - if ( isShowObject( object ) ) { - - var bFound = false; - - for ( var i = 0; i < selectedMeshes.length; i ++ ) { - - var selectedObjectId = selectedMeshes[ i ].id; - - if ( selectedObjectId === object.id ) { - - bFound = true; - break; - - } - - } - - if ( ! bFound ) { - - var visibility = object.visible; - - if ( ! bVisible || object.bVisible ) object.visible = bVisible; - - object.bVisible = visibility; - - } - - } - - } - - this.renderScene.traverse( VisibilityChangeCallBack ); - - }, - - updateTextureMatrix: function () { - - this.textureMatrix.set( 0.5, 0.0, 0.0, 0.5, - 0.0, 0.5, 0.0, 0.5, - 0.0, 0.0, 0.5, 0.5, - 0.0, 0.0, 0.0, 1.0 ); - this.textureMatrix.multiply( this.renderCamera.projectionMatrix ); - this.textureMatrix.multiply( this.renderCamera.matrixWorldInverse ); - - }, - - render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) { - this.renderObjects.length = 0; - if ( this.selectedObjects.length > 0 ) { - //缓存renderObjects - let renderList = renderer.renderLists.get(this.renderScene, this.renderCamera); - this.renderObjects = [...renderList.transparent.map(o => o.object), ...renderList.opaque.map(o => o.object)]; - - this.oldClearColor.copy( renderer.getClearColor() ); - this.oldClearAlpha = renderer.getClearAlpha(); - var oldAutoClear = renderer.autoClear; - - renderer.autoClear = false; - - if ( maskActive ) renderer.context.disable( renderer.context.STENCIL_TEST ); - - renderer.setClearColor( 0xffffff, 1 ); - - // Make selected objects invisible - this.changeVisibilityOfSelectedObjects( false ); - - var currentBackground = this.renderScene.background; - this.renderScene.background = null; - - // 1. Draw Non Selected objects in the depth buffer - this.renderScene.overrideMaterial = this.depthMaterial; - renderer.render(this.renderScene, this.renderCamera, this.renderTargetDepthBuffer, true); - - // Make selected objects visible - this.changeVisibilityOfSelectedObjects( true ); - - // Update Texture Matrix for Depth compare - this.updateTextureMatrix(); - - // Make non selected objects invisible, and draw only the selected objects, by comparing the depth buffer of non selected objects - this.changeVisibilityOfNonSelectedObjects( false ); - this.renderScene.overrideMaterial = this.prepareMaskMaterial; - this.prepareMaskMaterial.uniforms[ "cameraNearFar" ].value = new THREE.Vector2( this.renderCamera.near, this.renderCamera.far ); - this.prepareMaskMaterial.uniforms[ "depthTexture" ].value = this.renderTargetDepthBuffer.texture; - this.prepareMaskMaterial.uniforms[ "textureMatrix" ].value = this.textureMatrix; - renderer.render( this.renderScene, this.renderCamera, this.renderTargetMaskBuffer, true ); - this.renderScene.overrideMaterial = null; - this.changeVisibilityOfNonSelectedObjects( true ); - - this.renderScene.background = currentBackground; - - // 2. Downsample to Half resolution - this.quad.material = this.materialCopy; - this.copyUniforms[ "tDiffuse" ].value = this.renderTargetMaskBuffer.texture; - renderer.render( this.scene, this.camera, this.renderTargetMaskDownSampleBuffer, true ); - - this.tempPulseColor1.copy( this.visibleEdgeColor ); - this.tempPulseColor2.copy( this.hiddenEdgeColor ); - - if ( this.pulsePeriod > 0 ) { - - var scalar = ( 1 + 0.25 ) / 2 + Math.cos( performance.now() * 0.01 / this.pulsePeriod ) * ( 1.0 - 0.25 ) / 2; - this.tempPulseColor1.multiplyScalar( scalar ); - this.tempPulseColor2.multiplyScalar( scalar ); - - } - - // 3. Apply Edge Detection Pass - this.quad.material = this.edgeDetectionMaterial; - this.edgeDetectionMaterial.uniforms[ "maskTexture" ].value = this.renderTargetMaskDownSampleBuffer.texture; - this.edgeDetectionMaterial.uniforms[ "texSize" ].value = new THREE.Vector2( this.renderTargetMaskDownSampleBuffer.width, this.renderTargetMaskDownSampleBuffer.height ); - this.edgeDetectionMaterial.uniforms[ "visibleEdgeColor" ].value = this.tempPulseColor1; - this.edgeDetectionMaterial.uniforms[ "hiddenEdgeColor" ].value = this.tempPulseColor2; - renderer.render( this.scene, this.camera, this.renderTargetEdgeBuffer1, true ); - - // 4. Apply Blur on Half res - this.quad.material = this.separableBlurMaterial1; - this.separableBlurMaterial1.uniforms[ "colorTexture" ].value = this.renderTargetEdgeBuffer1.texture; - this.separableBlurMaterial1.uniforms[ "direction" ].value = THREE.OutlinePass.BlurDirectionX; - this.separableBlurMaterial1.uniforms[ "kernelRadius" ].value = this.edgeThickness; - renderer.render( this.scene, this.camera, this.renderTargetBlurBuffer1, true ); - this.separableBlurMaterial1.uniforms[ "colorTexture" ].value = this.renderTargetBlurBuffer1.texture; - this.separableBlurMaterial1.uniforms[ "direction" ].value = THREE.OutlinePass.BlurDirectionY; - renderer.render( this.scene, this.camera, this.renderTargetEdgeBuffer1, true ); - - // Apply Blur on quarter res - this.quad.material = this.separableBlurMaterial2; - this.separableBlurMaterial2.uniforms[ "colorTexture" ].value = this.renderTargetEdgeBuffer1.texture; - this.separableBlurMaterial2.uniforms[ "direction" ].value = THREE.OutlinePass.BlurDirectionX; - renderer.render( this.scene, this.camera, this.renderTargetBlurBuffer2, true ); - this.separableBlurMaterial2.uniforms[ "colorTexture" ].value = this.renderTargetBlurBuffer2.texture; - this.separableBlurMaterial2.uniforms[ "direction" ].value = THREE.OutlinePass.BlurDirectionY; - renderer.render( this.scene, this.camera, this.renderTargetEdgeBuffer2, true ); - - // Blend it additively over the input texture - this.quad.material = this.overlayMaterial; - this.overlayMaterial.uniforms[ "maskTexture" ].value = this.renderTargetMaskBuffer.texture; - this.overlayMaterial.uniforms[ "edgeTexture1" ].value = this.renderTargetEdgeBuffer1.texture; - this.overlayMaterial.uniforms[ "edgeTexture2" ].value = this.renderTargetEdgeBuffer2.texture; - this.overlayMaterial.uniforms[ "patternTexture" ].value = this.patternTexture; - this.overlayMaterial.uniforms[ "edgeStrength" ].value = this.edgeStrength; - this.overlayMaterial.uniforms[ "edgeGlow" ].value = this.edgeGlow; - this.overlayMaterial.uniforms[ "usePatternTexture" ].value = this.usePatternTexture; - - - if ( maskActive ) renderer.context.enable( renderer.context.STENCIL_TEST ); - - renderer.render( this.scene, this.camera, readBuffer, false ); - - renderer.setClearColor( this.oldClearColor, this.oldClearAlpha ); - renderer.autoClear = oldAutoClear; - - } - - if ( this.renderToScreen ) { - - this.quad.material = this.materialCopy; - this.copyUniforms[ "tDiffuse" ].value = readBuffer.texture; - renderer.render( this.scene, this.camera ); - - } - - }, - - getPrepareMaskMaterial: function () { - - return new THREE.ShaderMaterial( { - - uniforms: { - "depthTexture": { value: null }, - "cameraNearFar": { value: new THREE.Vector2( 0.5, 0.5 ) }, - "textureMatrix": { value: new THREE.Matrix4() } - }, - - vertexShader: [ - 'varying vec4 projTexCoord;', - 'varying vec4 vPosition;', - 'uniform mat4 textureMatrix;', - - 'void main() {', - - ' vPosition = modelViewMatrix * vec4( position, 1.0 );', - ' vec4 worldPosition = modelMatrix * vec4( position, 1.0 );', - ' projTexCoord = textureMatrix * worldPosition;', - ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', - - '}' - ].join( '\n' ), - - fragmentShader: [ - '#include ', - 'varying vec4 vPosition;', - 'varying vec4 projTexCoord;', - 'uniform sampler2D depthTexture;', - 'uniform vec2 cameraNearFar;', - - 'void main() {', - - ' float depth = unpackRGBAToDepth(texture2DProj( depthTexture, projTexCoord ));', - ' float viewZ = - DEPTH_TO_VIEW_Z( depth, cameraNearFar.x, cameraNearFar.y );', - ' float depthTest = (-vPosition.z > viewZ) ? 1.0 : 0.0;', - ' gl_FragColor = vec4(0.0, depthTest, 1.0, 1.0);', - - '}' - ].join( '\n' ) - - } ); - - }, - - getEdgeDetectionMaterial: function () { - - return new THREE.ShaderMaterial( { - - uniforms: { - "maskTexture": { value: null }, - "texSize": { value: new THREE.Vector2( 0.5, 0.5 ) }, - "visibleEdgeColor": { value: new THREE.Vector3( 1.0, 1.0, 1.0 ) }, - "hiddenEdgeColor": { value: new THREE.Vector3( 1.0, 1.0, 1.0 ) }, - }, - - vertexShader: - "varying vec2 vUv;\n\ - void main() {\n\ - vUv = uv;\n\ - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\ - }", - - fragmentShader: - "varying vec2 vUv;\ - uniform sampler2D maskTexture;\ - uniform vec2 texSize;\ - uniform vec3 visibleEdgeColor;\ - uniform vec3 hiddenEdgeColor;\ - \ - void main() {\n\ - vec2 invSize = 1.0 / texSize;\ - vec4 uvOffset = vec4(1.0, 0.0, 0.0, 1.0) * vec4(invSize, invSize);\ - vec4 c1 = texture2D( maskTexture, vUv + uvOffset.xy);\ - vec4 c2 = texture2D( maskTexture, vUv - uvOffset.xy);\ - vec4 c3 = texture2D( maskTexture, vUv + uvOffset.yw);\ - vec4 c4 = texture2D( maskTexture, vUv - uvOffset.yw);\ - float diff1 = (c1.r - c2.r)*0.5;\ - float diff2 = (c3.r - c4.r)*0.5;\ - float d = length( vec2(diff1, diff2) );\ - float a1 = min(c1.g, c2.g);\ - float a2 = min(c3.g, c4.g);\ - float visibilityFactor = min(a1, a2);\ - vec3 edgeColor = 1.0 - visibilityFactor > 0.001 ? visibleEdgeColor : hiddenEdgeColor;\ - gl_FragColor = vec4(edgeColor, 1.0) * vec4(d);\ - }" - } ); - - }, - - getSeperableBlurMaterial: function ( maxRadius ) { - - return new THREE.ShaderMaterial( { - - defines: { - "MAX_RADIUS": maxRadius, - }, - - uniforms: { - "colorTexture": { value: null }, - "texSize": { value: new THREE.Vector2( 0.5, 0.5 ) }, - "direction": { value: new THREE.Vector2( 0.5, 0.5 ) }, - "kernelRadius": { value: 1.0 } - }, - - vertexShader: - "varying vec2 vUv;\n\ - void main() {\n\ - vUv = uv;\n\ - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\ - }", - - fragmentShader: - "#include \ - varying vec2 vUv;\ - uniform sampler2D colorTexture;\ - uniform vec2 texSize;\ - uniform vec2 direction;\ - uniform float kernelRadius;\ - \ - float gaussianPdf(in float x, in float sigma) {\ - return 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma;\ - }\ - void main() {\ - vec2 invSize = 1.0 / texSize;\ - float weightSum = gaussianPdf(0.0, kernelRadius);\ - vec3 diffuseSum = texture2D( colorTexture, vUv).rgb * weightSum;\ - vec2 delta = direction * invSize * kernelRadius/float(MAX_RADIUS);\ - vec2 uvOffset = delta;\ - for( int i = 1; i <= MAX_RADIUS; i ++ ) {\ - float w = gaussianPdf(uvOffset.x, kernelRadius);\ - vec3 sample1 = texture2D( colorTexture, vUv + uvOffset).rgb;\ - vec3 sample2 = texture2D( colorTexture, vUv - uvOffset).rgb;\ - diffuseSum += ((sample1 + sample2) * w);\ - weightSum += (2.0 * w);\ - uvOffset += delta;\ - }\ - gl_FragColor = vec4(diffuseSum/weightSum, 1.0);\ - }" - } ); - - }, - - getOverlayMaterial: function () { - - return new THREE.ShaderMaterial( { - - uniforms: { - "maskTexture": { value: null }, - "edgeTexture1": { value: null }, - "edgeTexture2": { value: null }, - "patternTexture": { value: null }, - "edgeStrength": { value: 1.0 }, - "edgeGlow": { value: 1.0 }, - "usePatternTexture": { value: 0.0 } - }, - - vertexShader: - "varying vec2 vUv;\n\ - void main() {\n\ - vUv = uv;\n\ - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\ - }", - - fragmentShader: - "varying vec2 vUv;\ - uniform sampler2D maskTexture;\ - uniform sampler2D edgeTexture1;\ - uniform sampler2D edgeTexture2;\ - uniform sampler2D patternTexture;\ - uniform float edgeStrength;\ - uniform float edgeGlow;\ - uniform bool usePatternTexture;\ - \ - void main() {\ - vec4 edgeValue1 = texture2D(edgeTexture1, vUv);\ - vec4 edgeValue2 = texture2D(edgeTexture2, vUv);\ - vec4 maskColor = texture2D(maskTexture, vUv);\ - vec4 patternColor = texture2D(patternTexture, 6.0 * vUv);\ - float visibilityFactor = 1.0 - maskColor.g > 0.0 ? 1.0 : 0.5;\ - vec4 edgeValue = edgeValue1 + edgeValue2 * edgeGlow;\ - vec4 finalColor = edgeStrength * maskColor.r * edgeValue;\ - if(usePatternTexture)\ - finalColor += + visibilityFactor * (1.0 - maskColor.r) * (1.0 - patternColor.r);\ - gl_FragColor = finalColor;\ - }", - blending: THREE.AdditiveBlending, - depthTest: false, - depthWrite: false, - transparent: true - } ); - - } - -} ); - -THREE.OutlinePass.BlurDirectionX = new THREE.Vector2( 1.0, 0.0 ); -THREE.OutlinePass.BlurDirectionY = new THREE.Vector2( 0.0, 1.0 ); diff --git a/src/GraphicsSystem/OutlinePass.ts b/src/GraphicsSystem/OutlinePass.ts new file mode 100644 index 000000000..44f250406 --- /dev/null +++ b/src/GraphicsSystem/OutlinePass.ts @@ -0,0 +1,658 @@ +import { AdditiveBlending, Camera, Color, DoubleSide, LinearFilter, Matrix4, Mesh, MeshBasicMaterial, MeshDepthMaterial, NoBlending, Object3D, OrthographicCamera, PlaneBufferGeometry, RGBADepthPacking, RGBAFormat, Scene, ShaderMaterial, UniformsUtils, Vector2, Vector3, WebGLRenderTarget } from "three"; +import { Pass } from "three/examples/jsm/postprocessing/Pass"; +import { CopyShader } from "three/examples/jsm/shaders/CopyShader"; + +/** + * @author spidersharma / http://eduperiment.com/ + */ +export class OutlinePass extends Pass +{ + renderObjects: Object3D[]; + renderScene: Scene; + renderCamera: Camera; + selectedObjects: Object3D[]; + visibleEdgeColor: Color; + hiddenEdgeColor: Color; + edgeGlow: number; + usePatternTexture: boolean; + edgeThickness: number; + edgeStrength: number; + downSampleRatio: number; + pulsePeriod: number; + resolution: Vector2; + maskBufferMaterial: MeshBasicMaterial; + renderTargetMaskBuffer: WebGLRenderTarget; + depthMaterial: MeshDepthMaterial; + prepareMaskMaterial: ShaderMaterial; + renderTargetDepthBuffer: WebGLRenderTarget; + renderTargetMaskDownSampleBuffer: WebGLRenderTarget; + renderTargetBlurBuffer1: WebGLRenderTarget; + renderTargetBlurBuffer2: WebGLRenderTarget; + edgeDetectionMaterial: ShaderMaterial; + renderTargetEdgeBuffer1: WebGLRenderTarget; + renderTargetEdgeBuffer2: WebGLRenderTarget; + separableBlurMaterial1: ShaderMaterial; + separableBlurMaterial2: ShaderMaterial; + overlayMaterial: ShaderMaterial; + copyUniforms: { [x: string]: { value: any; }; }; + materialCopy: ShaderMaterial; + oldClearColor: Color; + oldClearAlpha: number; + fsQuad: Pass.FullScreenQuad; + tempPulseColor1: Color; + tempPulseColor2: Color; + textureMatrix: Matrix4; + patternTexture: any; + constructor(resolution: Vector2, scene: Scene, camera: Camera, selectedObjects?: Object3D[]) + { + super(); + + this.renderObjects = []; + + this.renderScene = scene; + this.renderCamera = camera; + this.selectedObjects = selectedObjects !== undefined ? selectedObjects : []; + this.visibleEdgeColor = new Color(1, 1, 1); + this.hiddenEdgeColor = new Color(0.1, 0.04, 0.02); + this.edgeGlow = 0.0; + this.usePatternTexture = false; + this.edgeThickness = 1.0; + this.edgeStrength = 3.0; + this.downSampleRatio = 2; + this.pulsePeriod = 0; + + this.resolution = (resolution !== undefined) ? new Vector2(resolution.x, resolution.y) : new Vector2(256, 256); + + var pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat }; + + var resx = Math.round(this.resolution.x / this.downSampleRatio); + var resy = Math.round(this.resolution.y / this.downSampleRatio); + + this.maskBufferMaterial = new MeshBasicMaterial({ color: 0xffffff }); + this.maskBufferMaterial.side = DoubleSide; + this.renderTargetMaskBuffer = new WebGLRenderTarget(this.resolution.x, this.resolution.y, pars); + this.renderTargetMaskBuffer.texture.name = "OutlinePass.mask"; + this.renderTargetMaskBuffer.texture.generateMipmaps = false; + + this.depthMaterial = new MeshDepthMaterial(); + this.depthMaterial.side = DoubleSide; + this.depthMaterial.depthPacking = RGBADepthPacking; + this.depthMaterial.blending = NoBlending; + + this.prepareMaskMaterial = this.getPrepareMaskMaterial(); + this.prepareMaskMaterial.side = DoubleSide; + this.prepareMaskMaterial.fragmentShader = replaceDepthToViewZ(this.prepareMaskMaterial.fragmentShader, this.renderCamera); + + this.renderTargetDepthBuffer = new WebGLRenderTarget(this.resolution.x, this.resolution.y, pars); + this.renderTargetDepthBuffer.texture.name = "OutlinePass.depth"; + this.renderTargetDepthBuffer.texture.generateMipmaps = false; + + this.renderTargetMaskDownSampleBuffer = new WebGLRenderTarget(resx, resy, pars); + this.renderTargetMaskDownSampleBuffer.texture.name = "OutlinePass.depthDownSample"; + this.renderTargetMaskDownSampleBuffer.texture.generateMipmaps = false; + + this.renderTargetBlurBuffer1 = new WebGLRenderTarget(resx, resy, pars); + this.renderTargetBlurBuffer1.texture.name = "OutlinePass.blur1"; + this.renderTargetBlurBuffer1.texture.generateMipmaps = false; + this.renderTargetBlurBuffer2 = new WebGLRenderTarget(Math.round(resx / 2), Math.round(resy / 2), pars); + this.renderTargetBlurBuffer2.texture.name = "OutlinePass.blur2"; + this.renderTargetBlurBuffer2.texture.generateMipmaps = false; + + this.edgeDetectionMaterial = this.getEdgeDetectionMaterial(); + this.renderTargetEdgeBuffer1 = new WebGLRenderTarget(resx, resy, pars); + this.renderTargetEdgeBuffer1.texture.name = "OutlinePass.edge1"; + this.renderTargetEdgeBuffer1.texture.generateMipmaps = false; + this.renderTargetEdgeBuffer2 = new WebGLRenderTarget(Math.round(resx / 2), Math.round(resy / 2), pars); + this.renderTargetEdgeBuffer2.texture.name = "OutlinePass.edge2"; + this.renderTargetEdgeBuffer2.texture.generateMipmaps = false; + + var MAX_EDGE_THICKNESS = 4; + var MAX_EDGE_GLOW = 4; + + this.separableBlurMaterial1 = this.getSeperableBlurMaterial(MAX_EDGE_THICKNESS); + this.separableBlurMaterial1.uniforms["texSize"].value = new Vector2(resx, resy); + this.separableBlurMaterial1.uniforms["kernelRadius"].value = 1; + this.separableBlurMaterial2 = this.getSeperableBlurMaterial(MAX_EDGE_GLOW); + this.separableBlurMaterial2.uniforms["texSize"].value = new Vector2(Math.round(resx / 2), Math.round(resy / 2)); + this.separableBlurMaterial2.uniforms["kernelRadius"].value = MAX_EDGE_GLOW; + + // Overlay material + this.overlayMaterial = this.getOverlayMaterial(); + + // copy material + if (CopyShader === undefined) + console.error("OutlinePass relies on CopyShader"); + + var copyShader = CopyShader; + + this.copyUniforms = UniformsUtils.clone(copyShader.uniforms); + this.copyUniforms["opacity"].value = 1.0; + + this.materialCopy = new ShaderMaterial({ + uniforms: this.copyUniforms, + vertexShader: copyShader.vertexShader, + fragmentShader: copyShader.fragmentShader, + blending: NoBlending, + depthTest: false, + depthWrite: false, + transparent: true + }); + + this.enabled = true; + this.needsSwap = false; + + this.oldClearColor = new Color(); + this.oldClearAlpha = 1; + + this.fsQuad = new Pass.FullScreenQuad(null); + + this.tempPulseColor1 = new Color(); + this.tempPulseColor2 = new Color(); + this.textureMatrix = new Matrix4(); + + function replaceDepthToViewZ(string, camera) + { + + var type = camera.isPerspectiveCamera ? 'perspective' : 'orthographic'; + + return string.replace(/DEPTH_TO_VIEW_Z/g, type + 'DepthToViewZ'); + + } + + }; + + dispose() + { + + this.renderTargetMaskBuffer.dispose(); + this.renderTargetDepthBuffer.dispose(); + this.renderTargetMaskDownSampleBuffer.dispose(); + this.renderTargetBlurBuffer1.dispose(); + this.renderTargetBlurBuffer2.dispose(); + this.renderTargetEdgeBuffer1.dispose(); + this.renderTargetEdgeBuffer2.dispose(); + + } + + setSize(width, height) + { + + this.renderTargetMaskBuffer.setSize(width, height); + + var resx = Math.round(width / this.downSampleRatio); + var resy = Math.round(height / this.downSampleRatio); + this.renderTargetMaskDownSampleBuffer.setSize(resx, resy); + this.renderTargetBlurBuffer1.setSize(resx, resy); + this.renderTargetEdgeBuffer1.setSize(resx, resy); + this.separableBlurMaterial1.uniforms["texSize"].value = new Vector2(resx, resy); + + resx = Math.round(resx / 2); + resy = Math.round(resy / 2); + + this.renderTargetBlurBuffer2.setSize(resx, resy); + this.renderTargetEdgeBuffer2.setSize(resx, resy); + + this.separableBlurMaterial2.uniforms["texSize"].value = new Vector2(resx, resy); + + } + + + changeVisibilityOfSelectedObjects(bVisible) + { + + function gatherSelectedMeshesCallBack(object) + { + + if (isShowObject(object)) + { + + if (bVisible) + { + + object.visible = object.userData.oldVisible; + delete object.userData.oldVisible; + + } else + { + + object.userData.oldVisible = object.visible; + object.visible = bVisible; + + } + + } + + } + + for (var i = 0; i < this.selectedObjects.length; i++) + { + + var selectedObject = this.selectedObjects[i]; + selectedObject.traverse(gatherSelectedMeshesCallBack); + + } + + } + + changeVisibilityOfNonSelectedObjects(bVisible) + { + + var selectedMeshes = []; + + function gatherSelectedMeshesCallBack(object) + { + + if (isShowObject(object)) selectedMeshes.push(object); + + } + + for (var i = 0; i < this.selectedObjects.length; i++) + { + + var selectedObject = this.selectedObjects[i]; + selectedObject.traverse(gatherSelectedMeshesCallBack); + + } + + function VisibilityChangeCallBack(object) + { + + if (isShowObject(object)) + { + + var bFound = false; + + for (var i = 0; i < selectedMeshes.length; i++) + { + + var selectedObjectId = selectedMeshes[i].id; + + if (selectedObjectId === object.id) + { + + bFound = true; + break; + + } + + } + + if (!bFound) + { + + var visibility = object.visible; + + if (!bVisible || object.bVisible) object.visible = bVisible; + + object.bVisible = visibility; + + } + + } + + } + + this.renderScene.traverse(VisibilityChangeCallBack); + + } + + updateTextureMatrix() + { + + this.textureMatrix.set(0.5, 0.0, 0.0, 0.5, + 0.0, 0.5, 0.0, 0.5, + 0.0, 0.0, 0.5, 0.5, + 0.0, 0.0, 0.0, 1.0); + this.textureMatrix.multiply(this.renderCamera.projectionMatrix); + this.textureMatrix.multiply(this.renderCamera.matrixWorldInverse); + + } + + render(renderer, writeBuffer, readBuffer, delta, maskActive) + { + this.renderObjects.length = 0; + if (this.selectedObjects.length > 0) + { + //缓存renderObjects + let renderList = renderer.renderLists.get(this.renderScene, this.renderCamera); + this.renderObjects = [...renderList.transparent.map(o => o.object), ...renderList.opaque.map(o => o.object)]; + + this.oldClearColor.copy(renderer.getClearColor()); + this.oldClearAlpha = renderer.getClearAlpha(); + var oldAutoClear = renderer.autoClear; + + renderer.autoClear = false; + + if (maskActive) renderer.context.disable(renderer.context.STENCIL_TEST); + + renderer.setClearColor(0xffffff, 1); + + // Make selected objects invisible + this.changeVisibilityOfSelectedObjects(false); + + var currentBackground = this.renderScene.background; + this.renderScene.background = null; + + // 1. Draw Non Selected objects in the depth buffer + this.renderScene.overrideMaterial = this.depthMaterial; + renderer.setRenderTarget(this.renderTargetDepthBuffer); + renderer.clear(); + renderer.render(this.renderScene, this.renderCamera); + + // Make selected objects visible + this.changeVisibilityOfSelectedObjects(true); + + // Update Texture Matrix for Depth compare + this.updateTextureMatrix(); + + // Make non selected objects invisible, and draw only the selected objects, by comparing the depth buffer of non selected objects + this.changeVisibilityOfNonSelectedObjects(false); + this.renderScene.overrideMaterial = this.prepareMaskMaterial; + //@ts-ignore + this.prepareMaskMaterial.uniforms["cameraNearFar"].value = new Vector2(this.renderCamera.near, this.renderCamera.far); + this.prepareMaskMaterial.uniforms["depthTexture"].value = this.renderTargetDepthBuffer.texture; + this.prepareMaskMaterial.uniforms["textureMatrix"].value = this.textureMatrix; + renderer.setRenderTarget(this.renderTargetMaskBuffer); + renderer.clear(); + renderer.render(this.renderScene, this.renderCamera); + this.renderScene.overrideMaterial = null; + this.changeVisibilityOfNonSelectedObjects(true); + + this.renderScene.background = currentBackground; + + // 2. Downsample to Half resolution + this.fsQuad.material = this.materialCopy; + this.copyUniforms["tDiffuse"].value = this.renderTargetMaskBuffer.texture; + renderer.setRenderTarget(this.renderTargetMaskDownSampleBuffer); + renderer.clear(); + this.fsQuad.render(renderer); + + this.tempPulseColor1.copy(this.visibleEdgeColor); + this.tempPulseColor2.copy(this.hiddenEdgeColor); + + if (this.pulsePeriod > 0) + { + + var scalar = (1 + 0.25) / 2 + Math.cos(performance.now() * 0.01 / this.pulsePeriod) * (1.0 - 0.25) / 2; + this.tempPulseColor1.multiplyScalar(scalar); + this.tempPulseColor2.multiplyScalar(scalar); + + } + + // 3. Apply Edge Detection Pass + this.fsQuad.material = this.edgeDetectionMaterial; + this.edgeDetectionMaterial.uniforms["maskTexture"].value = this.renderTargetMaskDownSampleBuffer.texture; + this.edgeDetectionMaterial.uniforms["texSize"].value = new Vector2(this.renderTargetMaskDownSampleBuffer.width, this.renderTargetMaskDownSampleBuffer.height); + this.edgeDetectionMaterial.uniforms["visibleEdgeColor"].value = this.tempPulseColor1; + this.edgeDetectionMaterial.uniforms["hiddenEdgeColor"].value = this.tempPulseColor2; + renderer.setRenderTarget(this.renderTargetEdgeBuffer1); + renderer.clear(); + this.fsQuad.render(renderer); + + // 4. Apply Blur on Half res + this.fsQuad.material = this.separableBlurMaterial1; + this.separableBlurMaterial1.uniforms["colorTexture"].value = this.renderTargetEdgeBuffer1.texture; + this.separableBlurMaterial1.uniforms["direction"].value = OutlinePass.BlurDirectionX; + this.separableBlurMaterial1.uniforms["kernelRadius"].value = this.edgeThickness; + renderer.setRenderTarget(this.renderTargetBlurBuffer1); + renderer.clear(); + this.fsQuad.render(renderer); + this.separableBlurMaterial1.uniforms["colorTexture"].value = this.renderTargetBlurBuffer1.texture; + this.separableBlurMaterial1.uniforms["direction"].value = OutlinePass.BlurDirectionY; + renderer.setRenderTarget(this.renderTargetEdgeBuffer1); + renderer.clear(); + this.fsQuad.render(renderer); + + // Apply Blur on quarter res + this.fsQuad.material = this.separableBlurMaterial2; + this.separableBlurMaterial2.uniforms["colorTexture"].value = this.renderTargetEdgeBuffer1.texture; + this.separableBlurMaterial2.uniforms["direction"].value = OutlinePass.BlurDirectionX; + renderer.setRenderTarget(this.renderTargetBlurBuffer2); + renderer.clear(); + this.fsQuad.render(renderer); + this.separableBlurMaterial2.uniforms["colorTexture"].value = this.renderTargetBlurBuffer2.texture; + this.separableBlurMaterial2.uniforms["direction"].value = OutlinePass.BlurDirectionY; + renderer.setRenderTarget(this.renderTargetEdgeBuffer2); + renderer.clear(); + this.fsQuad.render(renderer); + + // Blend it additively over the input texture + this.fsQuad.material = this.overlayMaterial; + this.overlayMaterial.uniforms["maskTexture"].value = this.renderTargetMaskBuffer.texture; + this.overlayMaterial.uniforms["edgeTexture1"].value = this.renderTargetEdgeBuffer1.texture; + this.overlayMaterial.uniforms["edgeTexture2"].value = this.renderTargetEdgeBuffer2.texture; + this.overlayMaterial.uniforms["patternTexture"].value = this.patternTexture; + this.overlayMaterial.uniforms["edgeStrength"].value = this.edgeStrength; + this.overlayMaterial.uniforms["edgeGlow"].value = this.edgeGlow; + this.overlayMaterial.uniforms["usePatternTexture"].value = this.usePatternTexture; + + + if (maskActive) renderer.context.enable(renderer.context.STENCIL_TEST); + + renderer.setRenderTarget(readBuffer); + this.fsQuad.render(renderer); + + renderer.setClearColor(this.oldClearColor, this.oldClearAlpha); + renderer.autoClear = oldAutoClear; + + } + + if (this.renderToScreen) + { + + this.fsQuad.material = this.materialCopy; + this.copyUniforms["tDiffuse"].value = readBuffer.texture; + renderer.setRenderTarget(null); + this.fsQuad.render(renderer); + + } + } + + getPrepareMaskMaterial() + { + + return new ShaderMaterial({ + + uniforms: { + "depthTexture": { value: null }, + "cameraNearFar": { value: new Vector2(0.5, 0.5) }, + "textureMatrix": { value: new Matrix4() } + }, + + vertexShader: [ + 'varying vec4 projTexCoord;', + 'varying vec4 vPosition;', + 'uniform mat4 textureMatrix;', + + 'void main() {', + + ' vPosition = modelViewMatrix * vec4( position, 1.0 );', + ' vec4 worldPosition = modelMatrix * vec4( position, 1.0 );', + ' projTexCoord = textureMatrix * worldPosition;', + ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', + + '}' + ].join('\n'), + + fragmentShader: [ + '#include ', + 'varying vec4 vPosition;', + 'varying vec4 projTexCoord;', + 'uniform sampler2D depthTexture;', + 'uniform vec2 cameraNearFar;', + + 'void main() {', + + ' float depth = unpackRGBAToDepth(texture2DProj( depthTexture, projTexCoord ));', + ' float viewZ = - DEPTH_TO_VIEW_Z( depth, cameraNearFar.x, cameraNearFar.y );', + ' float depthTest = (-vPosition.z > viewZ) ? 1.0 : 0.0;', + ' gl_FragColor = vec4(0.0, depthTest, 1.0, 1.0);', + + '}' + ].join('\n') + + }); + + } + + getEdgeDetectionMaterial() + { + + return new ShaderMaterial({ + + uniforms: { + "maskTexture": { value: null }, + "texSize": { value: new Vector2(0.5, 0.5) }, + "visibleEdgeColor": { value: new Vector3(1.0, 1.0, 1.0) }, + "hiddenEdgeColor": { value: new Vector3(1.0, 1.0, 1.0) }, + }, + + vertexShader: + "varying vec2 vUv;\n\ + void main() {\n\ + vUv = uv;\n\ + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\ + }", + + fragmentShader: + "varying vec2 vUv;\ + uniform sampler2D maskTexture;\ + uniform vec2 texSize;\ + uniform vec3 visibleEdgeColor;\ + uniform vec3 hiddenEdgeColor;\ + \ + void main() {\n\ + vec2 invSize = 1.0 / texSize;\ + vec4 uvOffset = vec4(1.0, 0.0, 0.0, 1.0) * vec4(invSize, invSize);\ + vec4 c1 = texture2D( maskTexture, vUv + uvOffset.xy);\ + vec4 c2 = texture2D( maskTexture, vUv - uvOffset.xy);\ + vec4 c3 = texture2D( maskTexture, vUv + uvOffset.yw);\ + vec4 c4 = texture2D( maskTexture, vUv - uvOffset.yw);\ + float diff1 = (c1.r - c2.r)*0.5;\ + float diff2 = (c3.r - c4.r)*0.5;\ + float d = length( vec2(diff1, diff2) );\ + float a1 = min(c1.g, c2.g);\ + float a2 = min(c3.g, c4.g);\ + float visibilityFactor = min(a1, a2);\ + vec3 edgeColor = 1.0 - visibilityFactor > 0.001 ? visibleEdgeColor : hiddenEdgeColor;\ + gl_FragColor = vec4(edgeColor, 1.0) * vec4(d);\ + }" + }); + + } + + getSeperableBlurMaterial(maxRadius: number) + { + + return new ShaderMaterial({ + + defines: { + "MAX_RADIUS": maxRadius, + }, + + uniforms: { + "colorTexture": { value: null }, + "texSize": { value: new Vector2(0.5, 0.5) }, + "direction": { value: new Vector2(0.5, 0.5) }, + "kernelRadius": { value: 1.0 } + }, + + vertexShader: + "varying vec2 vUv;\n\ + void main() {\n\ + vUv = uv;\n\ + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\ + }", + + fragmentShader: + "#include \ + varying vec2 vUv;\ + uniform sampler2D colorTexture;\ + uniform vec2 texSize;\ + uniform vec2 direction;\ + uniform float kernelRadius;\ + \ + float gaussianPdf(in float x, in float sigma) {\ + return 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma;\ + }\ + void main() {\ + vec2 invSize = 1.0 / texSize;\ + float weightSum = gaussianPdf(0.0, kernelRadius);\ + vec3 diffuseSum = texture2D( colorTexture, vUv).rgb * weightSum;\ + vec2 delta = direction * invSize * kernelRadius/float(MAX_RADIUS);\ + vec2 uvOffset = delta;\ + for( int i = 1; i <= MAX_RADIUS; i ++ ) {\ + float w = gaussianPdf(uvOffset.x, kernelRadius);\ + vec3 sample1 = texture2D( colorTexture, vUv + uvOffset).rgb;\ + vec3 sample2 = texture2D( colorTexture, vUv - uvOffset).rgb;\ + diffuseSum += ((sample1 + sample2) * w);\ + weightSum += (2.0 * w);\ + uvOffset += delta;\ + }\ + gl_FragColor = vec4(diffuseSum/weightSum, 1.0);\ + }" + }); + + } + + getOverlayMaterial() + { + + return new ShaderMaterial({ + + uniforms: { + "maskTexture": { value: null }, + "edgeTexture1": { value: null }, + "edgeTexture2": { value: null }, + "patternTexture": { value: null }, + "edgeStrength": { value: 1.0 }, + "edgeGlow": { value: 1.0 }, + "usePatternTexture": { value: 0.0 } + }, + + vertexShader: + "varying vec2 vUv;\n\ + void main() {\n\ + vUv = uv;\n\ + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\ + }", + + fragmentShader: + "varying vec2 vUv;\ + uniform sampler2D maskTexture;\ + uniform sampler2D edgeTexture1;\ + uniform sampler2D edgeTexture2;\ + uniform sampler2D patternTexture;\ + uniform float edgeStrength;\ + uniform float edgeGlow;\ + uniform bool usePatternTexture;\ + \ + void main() {\ + vec4 edgeValue1 = texture2D(edgeTexture1, vUv);\ + vec4 edgeValue2 = texture2D(edgeTexture2, vUv);\ + vec4 maskColor = texture2D(maskTexture, vUv);\ + vec4 patternColor = texture2D(patternTexture, 6.0 * vUv);\ + float visibilityFactor = 1.0 - maskColor.g > 0.0 ? 1.0 : 0.5;\ + vec4 edgeValue = edgeValue1 + edgeValue2 * edgeGlow;\ + vec4 finalColor = edgeStrength * maskColor.r * edgeValue;\ + if(usePatternTexture)\ + finalColor += + visibilityFactor * (1.0 - maskColor.r) * (1.0 - patternColor.r);\ + gl_FragColor = finalColor;\ + }", + blending: AdditiveBlending, + depthTest: false, + depthWrite: false, + transparent: true + }); + + } + + static BlurDirectionX = new Vector2(1.0, 0.0); + static BlurDirectionY = new Vector2(0.0, 1.0); +} + +function isShowObject(object: Object3D) +{ + //@ts-ignore + return object.isMesh || object.isLine || object.isSprite; +} diff --git a/src/GraphicsSystem/PreViewer.ts b/src/GraphicsSystem/PreViewer.ts index 2a55d6702..b9dfbdfea 100644 --- a/src/GraphicsSystem/PreViewer.ts +++ b/src/GraphicsSystem/PreViewer.ts @@ -1,11 +1,10 @@ -import * as THREE from 'three'; +import { Line, LineBasicMaterial, LineDashedMaterial, LineSegments, Scene, Vector3, WebGLRenderer } from 'three'; +import { end } from 'xaop'; import { app } from '../ApplicationServices/Application'; -import { CameraUpdate } from './CameraUpdate'; -import { Cursor } from './Cursor'; -import { LineBasicMaterial, LineDashedMaterial, Line, BufferGeometry, Vector3, LineSegments } from 'three'; import { BufferGeometryUtils } from '../Geometry/BufferGeometryUtils'; import { PointShapeUtils } from '../Geometry/PointShapeUtils'; -import { end } from 'xaop'; +import { CameraUpdate } from './CameraUpdate'; +import { Cursor } from './Cursor'; export enum DrawMode { @@ -19,9 +18,9 @@ export class PreViewer { private m_bNeedUpdate: boolean; //渲染器 - private m_Render: THREE.WebGLRenderer; + private m_Render: WebGLRenderer; //场景 - private m_Scene = new THREE.Scene(); + private m_Scene = new Scene(); //相机控制 protected m_Camera: CameraUpdate; private m_Width: number; @@ -117,7 +116,7 @@ export class PreViewer this.m_DomEl.appendChild(canvas); - this.m_Render = new THREE.WebGLRenderer({ canvas: canvas, clearColor: 0, alpha: true }) + this.m_Render = new WebGLRenderer({ canvas: canvas, alpha: true }) } //#endregion diff --git a/src/GraphicsSystem/Viewer.ts b/src/GraphicsSystem/Viewer.ts index 79e6756d3..b7f45dbb4 100644 --- a/src/GraphicsSystem/Viewer.ts +++ b/src/GraphicsSystem/Viewer.ts @@ -1,7 +1,11 @@ -import { EffectComposer, Matrix4, Object3D, OutlinePass, RenderPass, Scene, SMAAPass, Vector2, Vector3, WebGLRenderer } from 'three'; +import { Matrix4, Object3D, Scene, Vector2, Vector3, WebGLRenderer } from 'three'; +import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer"; +import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass"; +import { SMAAPass } from "three/examples/jsm/postprocessing/SMAAPass"; import { begin, end } from 'xaop'; import { app } from '../ApplicationServices/Application'; import { arrayRemoveOnce } from '../Common/ArrayExt'; +import { IsDev } from '../Common/Deving'; import { GetEntity } from '../Common/Utils'; import { Database } from '../DatabaseServices/Database'; import { Entity } from '../DatabaseServices/Entity/Entity'; @@ -10,22 +14,10 @@ import { userConfig } from '../Editor/UserConfig'; import { GetBox, GetBoxArr, isPerpendicularityTo, ZeroVec } from '../Geometry/GeUtils'; import { PlaneExt } from '../Geometry/Plane'; import { CameraUpdate } from './CameraUpdate'; -import { FXAAPass } from './FXAAPass'; import { GripScene } from './GripScene'; +import { OutlinePass } from './OutlinePass'; import { PreViewer } from './PreViewer'; -//导入其他js模块 -require("three-CopyShader"); -require("three-SMAAShader"); - -require("three-EffectComposer"); - -require("three-RenderPass"); - -require("three-SMAAPass"); - -require("three-OutlinePass"); - export enum AAType { FXAA = 0,//快速近似抗锯齿(性能更好) @@ -50,7 +42,6 @@ export class Viewer RenderPass: RenderPass; OutlinePass: OutlinePass; Composer: EffectComposer; - private _EffectFXAAPass: FXAAPass; private _SMAAPass: SMAAPass; private _Scene: Scene = new Scene; @@ -79,8 +70,9 @@ export class Viewer this.InitRender(canvasContainer); this.StartRender(); - //默认西南等轴视图 - this.ViewToSwiso(); + if (!IsDev()) + //默认西南等轴视图 + this.ViewToSwiso(); let self = this; end(new Entity().Update, function () @@ -127,8 +119,6 @@ export class Viewer { if (this._AAType === AAType.FXAA) { - arrayRemoveOnce(this.Composer.passes, this._EffectFXAAPass); - if (!this._SMAAPass) { this._SMAAPass = new SMAAPass(this.Width, this.Height); @@ -138,10 +128,8 @@ export class Viewer this.Composer.addPass(this._SMAAPass); } else - { + //https://stackoverflow.com/questions/49767165/how-to-remove-fxaa-in-threejs-outline-example arrayRemoveOnce(this.Composer.passes, this._SMAAPass); - this.Composer.addPass(this._EffectFXAAPass); - } this._AAType = type; this.UpdateRender(); } @@ -193,10 +181,6 @@ export class Viewer this.Composer.addPass(this.OutlinePass); - this._EffectFXAAPass = new FXAAPass(); - - this.Composer.addPass(this._EffectFXAAPass); - this.OnSize(); } @@ -354,7 +338,6 @@ export class Viewer */ get VisibleObjects(): Object3D[] { - //@ts-ignore let renderObjects: Object3D[] = this.OutlinePass.renderObjects; if (renderObjects.length > 0) return renderObjects; diff --git a/src/Loader/FBXLoader.ts b/src/Loader/FBXLoader.ts index 6ca175a55..06a67046a 100644 --- a/src/Loader/FBXLoader.ts +++ b/src/Loader/FBXLoader.ts @@ -1,11 +1,13 @@ +import { Group, LoadingManager } from "three"; +import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader"; + //最小限度的使用这个模块. 所以提供了一个导出的头文件. -import * as THREE from "three"; require('three-FBXLoader'); export interface LoaderState { - State: boolean, - object?: THREE.Group + State: boolean; + object?: Group; } @@ -28,8 +30,8 @@ let onError = function (xhr) //Loader FBX export function loadFBX(fileName: string): Promise { - let manager = new THREE.LoadingManager(); - let loader = new THREE.FBXLoader(manager); + let manager = new LoadingManager(); + let loader = new FBXLoader(manager); return new Promise((resolve, reject) => diff --git a/src/csg/core/math/Vector2.ts b/src/csg/core/math/Vector2.ts index 4b7db69e4..f5dd5629b 100644 --- a/src/csg/core/math/Vector2.ts +++ b/src/csg/core/math/Vector2.ts @@ -10,7 +10,7 @@ export class Vector2D extends Vector2 } clone() { - return new Vector2D(this.x, this.y); + return new Vector2D(this.x, this.y) as this; } // returns the vector rotated by 90 degrees clockwise normal() diff --git a/src/csg/core/math/Vector3.ts b/src/csg/core/math/Vector3.ts index da9f7fff2..7ce25a496 100644 --- a/src/csg/core/math/Vector3.ts +++ b/src/csg/core/math/Vector3.ts @@ -6,7 +6,7 @@ export class Vector3D extends Vector3 { clone() { - return new Vector3D(this.x, this.y, this.z); + return new Vector3D(this.x, this.y, this.z) as this; } // find a vector that is somewhat perpendicular to this one randomNonParallelVector() diff --git a/utils/copy_type.ts b/utils/copy_type.ts index d55c7a455..a77222e74 100644 --- a/utils/copy_type.ts +++ b/utils/copy_type.ts @@ -6,39 +6,41 @@ import { downLoadFile } from './utils'; // copyFolderRecursiveSync("./@types/", "./node_modules/"); -function downloadTypes(downFiles) +function downloadTypes(downFiles: { moduleName: string; urlPath: string; files: string[]; }) { - let filePath = path.resolve("./node_modules/@types/" + downFiles.name) + "\\"; - console.log('filePath: ', filePath); + let filePath = path.resolve("./node_modules/" + downFiles.moduleName) + "\\"; for (let file of downFiles.files) { - console.log(downFiles.urlPath + file); try { downLoadFile(downFiles.urlPath + file, filePath + file); + console.log("成功", file); } catch (error) { - console.log('error: ', error); + console.log(file, error); } } } downloadTypes({ - name: "three", - urlPath: "https://gitee.com/BearCAD/DefinitelyType2/raw/master/three/", + moduleName: "three", + urlPath: "https://raw.githubusercontent.com/FishOrBear/three.js/patch-1/", files: [ - // "index.d.ts", - "three-core.d.ts", - // "three-outlinepass.d.ts", - // "three-smaapass.d.ts" + "examples/jsm/shaders/CopyShader.d.ts", + "examples/jsm/postprocessing/EffectComposer.d.ts", + "examples/jsm/postprocessing/RenderPass.d.ts", + "examples/jsm/shaders/FXAAShader.d.ts", + "src/helpers/SpotLightHelper.d.ts", + "src/helpers/RectAreaLightHelper.d.ts", + "examples/jsm/postprocessing/Pass.d.ts" ] }); downloadTypes({ - name: "jquery", + moduleName: "jquery", urlPath: "https://gitee.com/BearCAD/DefinitelyType2/raw/master/jquery/", files: [ "index.d.ts" diff --git a/utils/utils.ts b/utils/utils.ts index dc4cfcdd2..f6ff98264 100644 --- a/utils/utils.ts +++ b/utils/utils.ts @@ -9,7 +9,7 @@ export function downLoadFile(url: string, filePath: string) { if (fs.existsSync(filePath)) fs.unlinkSync(filePath); - else + else if (!fs.existsSync(path.dirname(filePath))) fs.mkdirSync(path.dirname(filePath)); let file = fs.createWriteStream(filePath); http.get(url, function (response)