diff --git a/packages/chili-core/src/config.ts b/packages/chili-core/src/config.ts index c2482ae0..2c4e31ab 100644 --- a/packages/chili-core/src/config.ts +++ b/packages/chili-core/src/config.ts @@ -4,12 +4,12 @@ import { Lazy, PubSub } from "./foundation"; import { ObjectSnapType } from "./snapType"; export const VisualConfig = { - defaultEdgeColor: 0x121314, + defaultEdgeColor: 0x111111, defaultFaceColor: 0xdedede, highlightEdgeColor: 0x0000ee, highlightFaceColor: 0x0000ee, - selectedEdgeColor: 0x0000ff, - selectedFaceColor: 0x0000ff, + selectedEdgeColor: 0x2222ff, + selectedFaceColor: 0x2222ff, editVertexSize: 7, editVertexColor: 0x0000ff, hintVertexSize: 5, diff --git a/packages/chili-three/src/outlinePass.js b/packages/chili-three/src/outlinePass.js index e4068402..e9bdd93d 100644 --- a/packages/chili-three/src/outlinePass.js +++ b/packages/chili-three/src/outlinePass.js @@ -1,12 +1,18 @@ +/** + * This is a forked and simplified version of the OutlinePass from three.js. In order to improve performance, various + * features have been deleted. This is about 2x faster. + */ import { AdditiveBlending, Color, DoubleSide, - HalfFloatType, + LinearFilter, Matrix4, + MeshBasicMaterial, MeshDepthMaterial, NoBlending, RGBADepthPacking, + RGBAFormat, ShaderMaterial, UniformsUtils, Vector2, @@ -16,7 +22,7 @@ import { import { FullScreenQuad, Pass } from "three/examples/jsm/postprocessing/Pass.js"; import { CopyShader } from "three/examples/jsm/shaders/CopyShader.js"; -export class OutlinePass extends Pass { +class OutlinePass extends Pass { constructor(scene, camera) { super(); @@ -25,7 +31,6 @@ export class OutlinePass extends Pass { this.visibleEdgeColor = new Color(1, 1, 1); this.hiddenEdgeColor = new Color(0.1, 0.04, 0.02); this.usePatternTexture = false; - this.patternTexture = null; this.edgeStrength = 3.0; this.pulsePeriod = 0; @@ -33,7 +38,16 @@ export class OutlinePass extends Pass { this.resolution = new Vector2(256, 256); - this.renderTargetMaskBuffer = new WebGLRenderTarget(this.resolution.x, this.resolution.y); + const pars = { + minFilter: LinearFilter, + magFilter: LinearFilter, + format: RGBAFormat, + skipInvalidateFramebuffer: true, + }; + + 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; @@ -49,16 +63,12 @@ export class OutlinePass extends Pass { this.renderCamera, ); - this.renderTargetDepthBuffer = new WebGLRenderTarget(this.resolution.x, this.resolution.y, { - type: HalfFloatType, - }); + this.renderTargetDepthBuffer = new WebGLRenderTarget(this.resolution.x, this.resolution.y, pars); this.renderTargetDepthBuffer.texture.name = "OutlinePass.depth"; this.renderTargetDepthBuffer.texture.generateMipmaps = false; this.edgeDetectionMaterial = this.getEdgeDetectionMaterial(); - this.renderTargetEdgeBuffer1 = new WebGLRenderTarget(this.resolution.x, this.resolution.y, { - type: HalfFloatType, - }); + this.renderTargetEdgeBuffer1 = new WebGLRenderTarget(this.resolution.x, this.resolution.y, pars); this.renderTargetEdgeBuffer1.texture.name = "OutlinePass.edge1"; this.renderTargetEdgeBuffer1.texture.generateMipmaps = false; @@ -66,9 +76,12 @@ export class OutlinePass extends Pass { this.overlayMaterial = this.getOverlayMaterial(); // copy material + if (CopyShader === undefined) console.error("THREE.OutlinePass relies on CopyShader"); + const copyShader = CopyShader; this.copyUniforms = UniformsUtils.clone(copyShader.uniforms); + this.copyUniforms["opacity"].value = 1.0; this.materialCopy = new ShaderMaterial({ uniforms: this.copyUniforms, @@ -77,6 +90,7 @@ export class OutlinePass extends Pass { blending: NoBlending, depthTest: false, depthWrite: false, + transparent: true, }); this.enabled = true; @@ -92,7 +106,8 @@ export class OutlinePass extends Pass { this.textureMatrix = new Matrix4(); function replaceDepthToViewZ(string, camera) { - const type = camera.isPerspectiveCamera ? "perspective" : "orthographic"; + var type = camera.isPerspectiveCamera ? "perspective" : "orthographic"; + return string.replace(/DEPTH_TO_VIEW_Z/g, type + "DepthToViewZ"); } } @@ -101,14 +116,6 @@ export class OutlinePass extends Pass { this.renderTargetMaskBuffer.dispose(); this.renderTargetDepthBuffer.dispose(); this.renderTargetEdgeBuffer1.dispose(); - - this.depthMaterial.dispose(); - this.prepareMaskMaterial.dispose(); - this.edgeDetectionMaterial.dispose(); - this.overlayMaterial.dispose(); - this.materialCopy.dispose(); - - this.fsQuad.dispose(); } setSize(width, height) { @@ -188,9 +195,7 @@ export class OutlinePass extends Pass { this.fsQuad.material = this.overlayMaterial; this.overlayMaterial.uniforms["maskTexture"].value = this.renderTargetMaskBuffer.texture; this.overlayMaterial.uniforms["edgeTexture1"].value = this.renderTargetEdgeBuffer1.texture; - this.overlayMaterial.uniforms["patternTexture"].value = this.patternTexture; this.overlayMaterial.uniforms["edgeStrength"].value = this.edgeStrength; - this.overlayMaterial.uniforms["usePatternTexture"].value = this.usePatternTexture; if (maskActive) renderer.state.buffers.stencil.setTest(true); @@ -210,42 +215,28 @@ export class OutlinePass extends Pass { } getPrepareMaskMaterial() { - return new ShaderMaterial({ + const result = new ShaderMaterial({ uniforms: { depthTexture: { value: null }, cameraNearFar: { value: new Vector2(0.5, 0.5) }, textureMatrix: { value: null }, + id: { value: null }, }, vertexShader: `#include #include - varying vec4 projTexCoord; varying vec4 vPosition; uniform mat4 textureMatrix; - void main() { - #include #include #include #include #include - vPosition = mvPosition; - - vec4 worldPosition = vec4( transformed, 1.0 ); - - #ifdef USE_INSTANCING - - worldPosition = instanceMatrix * worldPosition; - - #endif - - worldPosition = modelMatrix * worldPosition; - + vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 ); projTexCoord = textureMatrix * worldPosition; - }`, fragmentShader: `#include @@ -253,16 +244,23 @@ export class OutlinePass extends Pass { varying vec4 projTexCoord; uniform sampler2D depthTexture; uniform vec2 cameraNearFar; - + uniform float id; 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); - + gl_FragColor = vec4(id/255.0, depthTest, 1.0, 1.0); }`, }); + + let idCounter = 0; + result.onBeforeRender = (renderer, scene, camera, geometry, object, group) => { + result.uniforms.id.value = idCounter; + result.uniformsNeedUpdate = true; + idCounter += 100; + idCounter %= 255; + }; + return result; } getEdgeDetectionMaterial() { @@ -275,21 +273,18 @@ export class OutlinePass extends Pass { }, vertexShader: `varying vec2 vUv; - void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }`, fragmentShader: `varying vec2 vUv; - uniform sampler2D maskTexture; uniform vec2 texSize; uniform vec3 visibleEdgeColor; uniform vec3 hiddenEdgeColor; - void main() { - vec2 invSize = 1.0 / texSize; + vec2 invSize = 1. / 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); @@ -302,7 +297,7 @@ export class OutlinePass extends Pass { 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); + gl_FragColor = vec4(edgeColor, 1.0) * vec4(d > 0. ? 1. : 0.); }`, }); } @@ -314,32 +309,24 @@ export class OutlinePass extends Pass { edgeTexture1: { value: null }, patternTexture: { value: null }, edgeStrength: { value: 1.0 }, - usePatternTexture: { value: 0.0 }, }, vertexShader: `varying vec2 vUv; - void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }`, fragmentShader: `varying vec2 vUv; - uniform sampler2D maskTexture; uniform sampler2D edgeTexture1; uniform sampler2D patternTexture; uniform float edgeStrength; - uniform bool usePatternTexture; - void main() { vec4 edgeValue = texture2D(edgeTexture1, 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 finalColor = edgeStrength * edgeValue; - if(usePatternTexture) - finalColor += + visibilityFactor * (1.0 - maskColor.r) * (1.0 - patternColor.r); gl_FragColor = finalColor; }`, blending: AdditiveBlending, @@ -349,3 +336,5 @@ export class OutlinePass extends Pass { }); } } + +export { OutlinePass }; diff --git a/packages/chili-three/src/threeGeometry.ts b/packages/chili-three/src/threeGeometry.ts index 4cfd1115..d777eca9 100644 --- a/packages/chili-three/src/threeGeometry.ts +++ b/packages/chili-three/src/threeGeometry.ts @@ -7,6 +7,7 @@ import { IVisualGeometry, Matrix4, ShapeMeshData, + VisualConfig, } from "chili-core"; import { BufferGeometry, @@ -26,7 +27,10 @@ import { ThreeVisualContext } from "./threeVisualContext"; export class ThreeGeometry extends Object3D implements IVisualGeometry { private _faceMaterial: Material; - private _edgeMaterial = new LineBasicMaterial(); + private _edgeMaterial = new LineBasicMaterial({ + linewidth: 2, + color: VisualConfig.defaultEdgeColor, + }); private _edges?: LineSegments; private _faces?: Mesh; diff --git a/packages/chili-three/src/threeHighlighter.ts b/packages/chili-three/src/threeHighlighter.ts index ca667e3d..8275feae 100644 --- a/packages/chili-three/src/threeHighlighter.ts +++ b/packages/chili-three/src/threeHighlighter.ts @@ -104,10 +104,8 @@ export class GeometryState { this.geometry.removeTemperaryMaterial(); } else if (VisualState.hasState(newState, VisualState.highlighter)) { this.geometry.setEdgesMateiralTemperary(hilightEdgeMaterial); - this.geometry.setFacesMateiralTemperary(highlightFaceMaterial); } else if (VisualState.hasState(newState, VisualState.selected)) { this.geometry.setEdgesMateiralTemperary(selectedEdgeMaterial); - this.geometry.setFacesMateiralTemperary(selectedFaceMaterial); } this._states.set(key, [newState, this.geometry as any]); diff --git a/packages/chili-three/src/threeRenderBuilder.ts b/packages/chili-three/src/threeRenderBuilder.ts index d7367e80..843e9bfe 100644 --- a/packages/chili-three/src/threeRenderBuilder.ts +++ b/packages/chili-three/src/threeRenderBuilder.ts @@ -42,13 +42,9 @@ export class ThreeRenderBuilder { addOutlinePass(scene: Scene, color: number, usePatternTexture: boolean) { const outlinePass = new OutlinePass(scene, this.camera); - outlinePass.usePatternTexture = usePatternTexture; outlinePass.visibleEdgeColor = new Color(color).convertSRGBToLinear(); outlinePass.hiddenEdgeColor = new Color(color).convertSRGBToLinear(); outlinePass.edgeStrength = 5; - if (usePatternTexture) { - this.loadPatternTexture((t) => (outlinePass.patternTexture = t)); - } this.composer.addPass(outlinePass); return this; diff --git a/packages/chili-three/src/threeView.ts b/packages/chili-three/src/threeView.ts index cc6f8a24..83af7ab8 100644 --- a/packages/chili-three/src/threeView.ts +++ b/packages/chili-three/src/threeView.ts @@ -176,7 +176,8 @@ export class ThreeView extends Observable implements IView { }); if (!this._needsUpdate) return; - this.dynamicLight.position.copy(this.camera.position); + let dir = this.camera.position.clone().sub(this.cameraController.target); + this.dynamicLight.position.copy(dir); this._composer.render(); this._gizmo?.update(); diff --git a/packages/chili-three/src/threeVisual.ts b/packages/chili-three/src/threeVisual.ts index e376fd54..576b8b81 100644 --- a/packages/chili-three/src/threeVisual.ts +++ b/packages/chili-three/src/threeVisual.ts @@ -1,17 +1,8 @@ // Copyright 2022-2023 the Chili authors. All rights reserved. AGPL-3.0 license. -import { - IDisposable, - IDocument, - IEventHandler, - IHighlighter, - ITextGenerator, - IVisual, - Logger, - Plane, -} from "chili-core"; +import { IDisposable, IDocument, IEventHandler, ITextGenerator, IVisual, Logger, Plane } from "chili-core"; import { ModelSelectionHandler } from "chili-vis"; -import { AmbientLight, AxesHelper, Color, DirectionalLight, Object3D, Scene } from "three"; +import { AmbientLight, AxesHelper, Object3D, Scene } from "three"; import { ThreeHighlighter } from "./threeHighlighter"; import { ThreeTextGenerator } from "./threeTextGenerator"; import { ThreeView } from "./threeView"; @@ -52,11 +43,9 @@ export class ThreeVisual implements IVisual { initScene() { let scene = new Scene(); - scene.background = new Color(0x888888); - const light = new DirectionalLight(0xffffff, 0.5); let envLight = new AmbientLight(0x888888, 4); let axisHelper = new AxesHelper(250); - scene.add(light, envLight, axisHelper); + scene.add(envLight, axisHelper); return scene; }