#746 A lot of changes to Geometry. Started integrating RTE with the viewer

This commit is contained in:
AlexandruPopovici
2022-05-17 14:52:38 +03:00
parent b0a138fa6e
commit e3cd5ae1f0
14 changed files with 1026 additions and 35 deletions
+43 -3
View File
@@ -7,16 +7,18 @@ export default class Sandbox {
private pane: Pane
private tabs: any
private static urlParams = {
public static urlParams = {
url: 'https://latest.speckle.dev/streams/010b3af4c3/objects/a401baf38fe5809d0eb9d3c902a36e8f'
}
private static sceneParams = {
public static sceneParams = {
worldSize: {x: 0, y: 0, z: 0},
worldOrigin: {x:0, y:0, z:0},
exposure: 0.4,
tonemapping: 'Linear'
}
public constructor(viewer: IViewer) {
public constructor(viewer: Viewer) {
this.viewer = viewer
this.pane = new Pane({ title: 'Sandbox', expanded: true })
this.pane['containerElem_'].style.width = "300px";
@@ -30,6 +32,10 @@ export default class Sandbox {
});
}
public refresh() {
this.pane.refresh();
}
public makeGenericUI() {
this.tabs.pages[0].addInput(Sandbox.urlParams, 'url', {
title: 'url'
@@ -76,6 +82,40 @@ export default class Sandbox {
}
makeSceneUI() {
const worldFolder = this.tabs.pages[1].addFolder({
title: "World",
expanded: true
});
worldFolder.addInput(Sandbox.sceneParams.worldSize, 'x', {
disabled: true,
label: "Size-x",
step: 0.00000001
});
worldFolder.addInput(Sandbox.sceneParams.worldSize, 'y', {
disabled: true,
label: "Size-y",
step: 0.00000001
});
worldFolder.addInput(Sandbox.sceneParams.worldSize, 'z', {
disabled: true,
label: "Size-z",
step: 0.00000001
});
worldFolder.addSeparator();
worldFolder.addInput(Sandbox.sceneParams.worldOrigin, 'x', {
disabled: true,
label: "Origin-x"
});
worldFolder.addInput(Sandbox.sceneParams.worldOrigin, 'y', {
disabled: true,
label: "Origin-y"
});
worldFolder.addInput(Sandbox.sceneParams.worldOrigin, 'z', {
disabled: true,
label: "Origin-z"
});
this.tabs.pages[1].addSeparator();
const postFolder = this.tabs.pages[1].addFolder({
title: "Post",
expanded: true
+13 -5
View File
@@ -11,18 +11,26 @@ if (!container) {
const viewer = new Viewer(container)
await viewer.init()
const sandbox = new Sandbox(viewer)
window.addEventListener('load', () => {
viewer.onWindowResize()
})
const sandbox = new Sandbox(viewer)
sandbox.makeGenericUI()
sandbox.makeSceneUI()
// Load demo object
sandbox.loadUrl('https://latest.speckle.dev/streams/921741b30b/commits/c5acd4ff91?c=%5B147162.48107,6396783.94314,18.1266,147140.22466,6396758.05514,9.41576,0,1%5D')
viewer.on('load-progress', (a: { progress: number; id: string; url: string }) => {
if (a.progress >= 1) {
viewer.onWindowResize()
}
})
viewer.on('load-complete', ()=> {
Object.assign(Sandbox.sceneParams.worldSize, viewer.worldSize);
Object.assign(Sandbox.sceneParams.worldOrigin, viewer.worldOrigin);
sandbox.refresh();
})
sandbox.makeGenericUI()
sandbox.makeSceneUI()
// Load demo object
sandbox.loadUrl('https://latest.speckle.dev/streams/3ed8357f29/commits/b21fb0dcf7')
@@ -3,9 +3,10 @@ import debounce from 'lodash.debounce'
import SceneObjects from './SceneObjects'
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js'
import { Line2 } from 'three/examples/jsm/lines/Line2.js'
import { Vector2 } from 'three'
import { Matrix4, Vector2, Vector3 } from 'three'
import { GEOMETRY_LINES_AS_TRIANGLES } from './converter/Geometry'
import SpeckleStandardMaterial from './materials/SpeckleStandardMaterial'
import SpeckleLineMaterial from './materials/SpeckleLineMaterial'
/**
* Manages objects and provides some convenience methods to focus on the entire scene, or one specific object.
*/
@@ -17,7 +18,7 @@ export default class SceneObjectManager {
this.sceneObjects = new SceneObjects(viewer)
this.solidMaterial = new THREE.MeshStandardMaterial({
this.solidMaterial = new SpeckleStandardMaterial({
color: 0x8d9194,
emissive: 0x0,
roughness: 1,
@@ -362,7 +363,7 @@ export default class SceneObjectManager {
makeLineMaterial() {
let lineMaterial
if (GEOMETRY_LINES_AS_TRIANGLES) {
lineMaterial = new LineMaterial({
lineMaterial = new SpeckleLineMaterial({
color: 0x7f7f7f,
linewidth: 1, // in world units with size attenuation, pixels otherwise
worldUnits: false,
+3 -1
View File
@@ -44,6 +44,7 @@ export default class SceneObjects {
// When the `appliedFilter` is null, scene will contain `allObjects`. Otherwise, `filteredObjects`
// This is to optimize the no-filter usecase, so we don't make an unnecessary clone of all the objects
this.objectsInScene = this.allObjects
this.scene.add(this.allObjects)
this.isBusy = true
@@ -289,7 +290,8 @@ export default class SceneObjects {
) {
// if ( mesh.type === 'Line' ) continue
// if ( groupedObjects.children.length >= 2 ) continue
groupedObjects.add(mesh.clone())
const clone = mesh.clone()
groupedObjects.add(clone)
continue
}
+18 -1
View File
@@ -9,12 +9,13 @@ import InteractionHandler from './InteractionHandler'
import CameraHandler from './context/CameraHanlder'
import SectionBox from './SectionBox'
import { Clock, CubeCamera } from 'three'
import { Box3, Clock, CubeCamera, Vector3 } from 'three'
import { Scene } from 'three'
import { WebGLRenderer } from 'three'
import { Assets } from './Assets'
import { Optional } from '../helpers/typeHelper'
import { DefaultViewerParams, IViewer, ViewerParams } from '../IViewer'
import { World } from './World'
export class Viewer extends EventEmitter implements IViewer {
private clock: Clock
@@ -34,6 +35,22 @@ export class Viewer extends EventEmitter implements IViewer {
public static Assets: Assets
private _worldSize: Box3 = new Box3()
private _worldOrigin: Vector3 = new Vector3()
public get worldSize() {
World.worldBox.getCenter(this._worldOrigin)
const size = new Vector3().subVectors(World.worldBox.max, World.worldBox.min)
return {
x: size.x,
y: size.y,
z: size.z
}
}
public get worldOrigin() {
return this._worldOrigin
}
public constructor(
container: HTMLElement,
params: ViewerParams = DefaultViewerParams
@@ -67,6 +67,7 @@ export default class ViewerObjectLoader {
let total = 0
let viewerLoads = 0
let firstObjectPromise = null
let parsedObjects = [] // Temporary until refactor
for await (const obj of this.loader.getObjectIterator()) {
if (this.cancel) {
this.viewer.emit('load-progress', {
@@ -85,7 +86,7 @@ export default class ViewerObjectLoader {
async (objectWrapper) => {
await this.converter.asyncPause()
objectWrapper.meta.__importedUrl = this.objectUrl
this.viewer.sceneManager.addObject(objectWrapper)
parsedObjects.push(objectWrapper) // Temporary until refactor
viewerLoads++
}
)
@@ -103,7 +104,13 @@ export default class ViewerObjectLoader {
await firstObjectPromise
}
// Temporary until refactor
for (var k = 0; k < parsedObjects.length; k++) {
await this.converter.asyncPause()
this.viewer.sceneManager.addObject(parsedObjects[k])
}
await this.viewer.sceneManager.postLoadFunction()
this.viewer.emit('load-complete')
if (viewerLoads === 0) {
console.warn(`Viewer: no 3d objects found in object ${this.objectId}`)
+28
View File
@@ -0,0 +1,28 @@
import { Box3 } from 'three'
export class World {
/* This will no longer exist when we have a scene tree */
private static readonly boxes: Array<Box3> = new Array<Box3>()
public static readonly worldBox: Box3 = new Box3()
public static expandWorld(box: Box3) {
World.boxes.push(box)
World.updateWorld()
}
public static reduceWorld(box: Box3) {
World.boxes.splice(World.boxes.indexOf(box), 1)
World.updateWorld()
}
public static updateWorld() {
World.worldBox.makeEmpty()
for (var k = 0; k < this.boxes.length; k++) {
World.worldBox.union(World.boxes[k])
}
}
public static resetWorld() {
World.worldBox.makeEmpty()
}
}
+117 -20
View File
@@ -1,12 +1,16 @@
import {
Box3,
BoxBufferGeometry,
BufferAttribute,
BufferGeometry,
Float32BufferAttribute,
Matrix4,
Uint16BufferAttribute,
Uint32BufferAttribute
Uint32BufferAttribute,
Vector3
} from 'three'
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js'
import { World } from '../World'
export const GEOMETRY_LINES_AS_TRIANGLES = true
export enum GeometryAttributes {
@@ -29,78 +33,138 @@ export interface GeometryData {
*/
export class Geometry {
static makePointGeometry(geometryData: GeometryData): BufferGeometry {
return Geometry.makeMeshGeometry(geometryData)
const geometry = Geometry.makeMeshGeometry(geometryData)
World.expandWorld(geometry.boundingBox)
return geometry
}
static makePointCloudGeometry(geometryData: GeometryData): BufferGeometry {
return Geometry.makeMeshGeometry(geometryData)
const geometry = Geometry.makeMeshGeometry(geometryData)
World.expandWorld(geometry.boundingBox)
return geometry
}
static makeMeshGeometry(geometryData: GeometryData): BufferGeometry {
if (geometryData.bakeTransform) {
Geometry.transformGeometryData(geometryData, geometryData.bakeTransform)
}
const buffer = new BufferGeometry()
const geometry = new BufferGeometry()
if (geometryData.attributes.INDEX) {
if (
geometryData.attributes.POSITION.length >= 65535 ||
geometryData.attributes.INDEX.length >= 65535
) {
buffer.setIndex(new Uint32BufferAttribute(geometryData.attributes.INDEX, 1))
geometry.setIndex(new Uint32BufferAttribute(geometryData.attributes.INDEX, 1))
} else {
buffer.setIndex(new Uint16BufferAttribute(geometryData.attributes.INDEX, 1))
geometry.setIndex(new Uint16BufferAttribute(geometryData.attributes.INDEX, 1))
}
}
const position_low = new Float32Array(geometryData.attributes.POSITION.length)
const position_high = new Float32Array(geometryData.attributes.POSITION.length)
if (geometryData.attributes.POSITION) {
buffer.setAttribute(
for (var k = 0; k < geometryData.attributes.POSITION.length; k++) {
const doubleValue = geometryData.attributes.POSITION[k]
if (doubleValue >= 0.0) {
const doubleHigh = Math.floor(doubleValue / 65536.0) * 65536.0
position_high[k] = doubleHigh
position_low[k] = doubleValue - doubleHigh
} else {
const doubleHigh = Math.floor(-doubleValue / 65536.0) * 65536.0
position_high[k] = -doubleHigh
position_low[k] = doubleValue + doubleHigh
}
}
geometry.setAttribute(
'position',
new Float32BufferAttribute(geometryData.attributes.POSITION, 3)
)
}
if (geometryData.attributes.COLOR) {
buffer.setAttribute(
geometry.setAttribute(
'color',
new Float32BufferAttribute(geometryData.attributes.COLOR, 3)
)
}
buffer.computeVertexNormals()
buffer.computeBoundingSphere()
geometry.computeVertexNormals()
geometry.computeBoundingSphere()
geometry.computeBoundingBox()
return buffer
World.expandWorld(geometry.boundingBox)
geometry.setAttribute('position_low', new Float32BufferAttribute(position_low, 3))
geometry.setAttribute('position_high', new Float32BufferAttribute(position_high, 3))
return geometry
}
static makeLineGeometry(geometryData: GeometryData) {
if (geometryData.bakeTransform) {
Geometry.transformGeometryData(geometryData, geometryData.bakeTransform)
}
let geometry: { boundingBox: Box3 }
if (GEOMETRY_LINES_AS_TRIANGLES) {
return this.makeLineGeometry_TRIANGLE(geometryData)
geometry = this.makeLineGeometry_TRIANGLE(geometryData)
} else {
return this.makeLineGeometry_LINE(geometryData)
geometry = this.makeLineGeometry_LINE(geometryData)
}
World.expandWorld(geometry.boundingBox)
return geometry
}
static makeLineGeometry_LINE(geometryData: GeometryData) {
const buffer = new BufferGeometry()
const geometry = new BufferGeometry()
if (geometryData.attributes.POSITION) {
buffer.setAttribute(
geometry.setAttribute(
'position',
new Float32BufferAttribute(geometryData.attributes.POSITION, 3)
)
if (geometryData.bakeTransform) {
buffer.attributes.position.applyMatrix4(geometryData.bakeTransform)
}
geometry.computeBoundingBox()
const position_low = new Float32Array(geometryData.attributes.POSITION.length)
const position_high = new Float32Array(geometryData.attributes.POSITION.length)
if (geometryData.attributes.POSITION) {
for (var k = 0; k < geometryData.attributes.POSITION.length; k++) {
const doubleValue = geometryData.attributes.POSITION[k]
if (doubleValue >= 0.0) {
const doubleHigh = Math.floor(doubleValue / 65536.0) * 65536.0
position_high[k] = doubleHigh
position_low[k] = doubleValue - doubleHigh
} else {
const doubleHigh = Math.floor(-doubleValue / 65536.0) * 65536.0
position_high[k] = -doubleHigh
position_low[k] = doubleValue + doubleHigh
}
}
}
return buffer
geometry.setAttribute('position_low', new Float32BufferAttribute(position_low, 3))
geometry.setAttribute('position_high', new Float32BufferAttribute(position_high, 3))
return geometry
}
static makeLineGeometry_TRIANGLE(geometryData: GeometryData) {
const geometry = new LineGeometry()
geometry.setPositions(geometryData.attributes.POSITION)
if (geometryData.attributes.COLOR) geometry.setColors(geometryData.attributes.COLOR)
geometry.computeBoundingBox()
const position_low = new Float32Array(geometryData.attributes.POSITION.length)
const position_high = new Float32Array(geometryData.attributes.POSITION.length)
for (var k = 0; k < geometryData.attributes.POSITION.length; k++) {
const doubleValue = geometryData.attributes.POSITION[k]
if (doubleValue >= 0.0) {
const doubleHigh = Math.floor(doubleValue / 65536.0) * 65536.0
position_high[k] = doubleHigh
position_low[k] = doubleValue - doubleHigh
} else {
const doubleHigh = Math.floor(-doubleValue / 65536.0) * 65536.0
position_high[k] = -doubleHigh
position_low[k] = doubleValue + doubleHigh
}
}
geometry.setAttribute('position_low', new Float32BufferAttribute(position_low, 3))
geometry.setAttribute('position_high', new Float32BufferAttribute(position_high, 3))
return geometry
}
@@ -215,4 +279,37 @@ export class Geometry {
}
return colors
}
public static DoubleToHighLow(input: Vector3, low: Vector3, high: Vector3) {
let doubleValue = input.x
if (doubleValue >= 0.0) {
const doubleHigh = Math.floor(doubleValue / 65536.0) * 65536.0
high.x = doubleHigh
low.x = doubleValue - doubleHigh
} else {
const doubleHigh = Math.floor(-doubleValue / 65536.0) * 65536.0
high.x = -doubleHigh
low.x = doubleValue + doubleHigh
}
doubleValue = input.y
if (doubleValue >= 0.0) {
const doubleHigh = Math.floor(doubleValue / 65536.0) * 65536.0
high.y = doubleHigh
low.y = doubleValue - doubleHigh
} else {
const doubleHigh = Math.floor(-doubleValue / 65536.0) * 65536.0
high.y = -doubleHigh
low.y = doubleValue + doubleHigh
}
doubleValue = input.z
if (doubleValue >= 0.0) {
const doubleHigh = Math.floor(doubleValue / 65536.0) * 65536.0
high.z = doubleHigh
low.z = doubleValue - doubleHigh
} else {
const doubleHigh = Math.floor(-doubleValue / 65536.0) * 65536.0
high.z = -doubleHigh
low.z = doubleValue + doubleHigh
}
}
}
@@ -0,0 +1,82 @@
import { speckle_line_vert } from './shaders/speckle-line-vert'
import { speckle_line_frag } from './shaders/speckle-line-frag'
import { UniformsUtils, ShaderLib, Vector3, MeshStandardMaterial } from 'three'
import { Matrix4 } from 'three'
import { Geometry } from '../converter/Geometry'
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js'
class SpeckleLineMaterial extends LineMaterial {
private static readonly matBuff: Matrix4 = new Matrix4()
constructor(parameters, defines = []) {
super(parameters)
this.userData.uViewer_high = {
value: new Vector3()
}
this.userData.uViewer_low = {
value: new Vector3()
}
;(this as any).vertProgram = speckle_line_vert
;(this as any).fragProgram = speckle_line_frag
;(this as any).uniforms = UniformsUtils.merge([
ShaderLib.line.uniforms,
{
uViewer_high: {
value: this.userData.uViewer_high.value
},
uViewer_low: {
value: this.userData.uViewer_low.value
}
}
])
this.onBeforeCompile = function (shader) {
shader.uniforms.uViewer_high = this.userData.uViewer_high
shader.uniforms.uViewer_low = this.userData.uViewer_low
shader.vertexShader = this.vertProgram
shader.fragmentShader = this.fragProgram
}
for (var k = 0; k < defines.length; k++) {
this.defines[defines[k]] = ''
}
}
copy(source) {
super.copy(source)
this.userData = {}
this.userData.uViewer_high = {
value: new Vector3()
}
this.userData.uViewer_low = {
value: new Vector3()
}
return this
}
onBeforeRender(_this, scene, camera, geometry, object, group) {
SpeckleLineMaterial.matBuff.copy(camera.matrixWorldInverse)
SpeckleLineMaterial.matBuff.elements[12] = 0
SpeckleLineMaterial.matBuff.elements[13] = 0
SpeckleLineMaterial.matBuff.elements[14] = 0
SpeckleLineMaterial.matBuff.multiply(object.matrixWorld)
object.modelViewMatrix.copy(SpeckleLineMaterial.matBuff)
let uViewer_low = new Vector3()
let uViewer_high = new Vector3()
let uViewer = new Vector3(
camera.matrixWorld.elements[12],
camera.matrixWorld.elements[13],
camera.matrixWorld.elements[14]
)
Geometry.DoubleToHighLow(uViewer, uViewer_low, uViewer_high)
this.userData.uViewer_high.value.copy(uViewer_high)
this.userData.uViewer_low.value.copy(uViewer_low)
this.needsUpdate = true
}
}
export default SpeckleLineMaterial
@@ -0,0 +1,81 @@
import { speckle_standard_vert } from './shaders/speckle-standard-vert'
import { speckle_standard_frag } from './shaders/speckle-standard-frag'
import { UniformsUtils, ShaderLib, Vector3, MeshStandardMaterial } from 'three'
import { Matrix4 } from 'three'
import { Geometry } from '../converter/Geometry'
class SpeckleStandardMaterial extends MeshStandardMaterial {
private static readonly matBuff: Matrix4 = new Matrix4()
constructor(parameters, defines = []) {
super(parameters)
this.userData.uViewer_high = {
value: new Vector3()
}
this.userData.uViewer_low = {
value: new Vector3()
}
;(this as any).vertProgram = speckle_standard_vert
;(this as any).fragProgram = speckle_standard_frag
;(this as any).uniforms = UniformsUtils.merge([
ShaderLib.standard.uniforms,
{
uViewer_high: {
value: this.userData.uViewer_high.value
},
uViewer_low: {
value: this.userData.uViewer_low.value
}
}
])
this.onBeforeCompile = function (shader) {
shader.uniforms.uViewer_high = this.userData.uViewer_high
shader.uniforms.uViewer_low = this.userData.uViewer_low
shader.vertexShader = this.vertProgram
shader.fragmentShader = this.fragProgram
}
for (var k = 0; k < defines.length; k++) {
this.defines[defines[k]] = ''
}
}
copy(source) {
super.copy(source)
this.userData = {}
this.userData.uViewer_high = {
value: new Vector3()
}
this.userData.uViewer_low = {
value: new Vector3()
}
return this
}
onBeforeRender(_this, scene, camera, geometry, object, group) {
SpeckleStandardMaterial.matBuff.copy(camera.matrixWorldInverse)
SpeckleStandardMaterial.matBuff.elements[12] = 0
SpeckleStandardMaterial.matBuff.elements[13] = 0
SpeckleStandardMaterial.matBuff.elements[14] = 0
SpeckleStandardMaterial.matBuff.multiply(object.matrixWorld)
object.modelViewMatrix.copy(SpeckleStandardMaterial.matBuff)
let uViewer_low = new Vector3()
let uViewer_high = new Vector3()
let uViewer = new Vector3(
camera.matrixWorld.elements[12],
camera.matrixWorld.elements[13],
camera.matrixWorld.elements[14]
)
Geometry.DoubleToHighLow(uViewer, uViewer_low, uViewer_high)
object.frustumCulled = false
this.userData.uViewer_high.value.copy(uViewer_high)
this.userData.uViewer_low.value.copy(uViewer_low)
this.needsUpdate = true
}
}
export default SpeckleStandardMaterial
@@ -0,0 +1,160 @@
export const speckle_line_frag = /* glsl */ `
uniform vec3 diffuse;
uniform float opacity;
uniform float linewidth;
#ifdef USE_DASH
uniform float dashOffset;
uniform float dashSize;
uniform float gapSize;
#endif
varying float vLineDistance;
#ifdef WORLD_UNITS
varying vec4 worldPos;
varying vec3 worldStart;
varying vec3 worldEnd;
#ifdef USE_DASH
varying vec2 vUv;
#endif
#else
varying vec2 vUv;
#endif
#include <common>
#include <color_pars_fragment>
#include <fog_pars_fragment>
#include <logdepthbuf_pars_fragment>
#include <clipping_planes_pars_fragment>
vec2 closestLineToLine(vec3 p1, vec3 p2, vec3 p3, vec3 p4) {
float mua;
float mub;
vec3 p13 = p1 - p3;
vec3 p43 = p4 - p3;
vec3 p21 = p2 - p1;
float d1343 = dot( p13, p43 );
float d4321 = dot( p43, p21 );
float d1321 = dot( p13, p21 );
float d4343 = dot( p43, p43 );
float d2121 = dot( p21, p21 );
float denom = d2121 * d4343 - d4321 * d4321;
float numer = d1343 * d4321 - d1321 * d4343;
mua = numer / denom;
mua = clamp( mua, 0.0, 1.0 );
mub = ( d1343 + d4321 * ( mua ) ) / d4343;
mub = clamp( mub, 0.0, 1.0 );
return vec2( mua, mub );
}
void main() {
#include <clipping_planes_fragment>
#ifdef USE_DASH
if ( vUv.y < - 1.0 || vUv.y > 1.0 ) discard; // discard endcaps
if ( mod( vLineDistance + dashOffset, dashSize + gapSize ) > dashSize ) discard; // todo - FIX
#endif
float alpha = opacity;
#ifdef WORLD_UNITS
// Find the closest points on the view ray and the line segment
vec3 rayEnd = normalize( worldPos.xyz ) * 1e5;
vec3 lineDir = worldEnd - worldStart;
vec2 params = closestLineToLine( worldStart, worldEnd, vec3( 0.0, 0.0, 0.0 ), rayEnd );
vec3 p1 = worldStart + lineDir * params.x;
vec3 p2 = rayEnd * params.y;
vec3 delta = p1 - p2;
float len = length( delta );
float norm = len / linewidth;
#ifndef USE_DASH
#ifdef USE_ALPHA_TO_COVERAGE
float dnorm = fwidth( norm );
alpha = 1.0 - smoothstep( 0.5 - dnorm, 0.5 + dnorm, norm );
#else
if ( norm > 0.5 ) {
discard;
}
#endif
#endif
#else
#ifdef USE_ALPHA_TO_COVERAGE
// artifacts appear on some hardware if a derivative is taken within a conditional
float a = vUv.x;
float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;
float len2 = a * a + b * b;
float dlen = fwidth( len2 );
if ( abs( vUv.y ) > 1.0 ) {
alpha = 1.0 - smoothstep( 1.0 - dlen, 1.0 + dlen, len2 );
}
#else
if ( abs( vUv.y ) > 1.0 ) {
float a = vUv.x;
float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;
float len2 = a * a + b * b;
if ( len2 > 1.0 ) discard;
}
#endif
#endif
vec4 diffuseColor = vec4( diffuse, alpha );
#include <logdepthbuf_fragment>
#include <color_fragment>
gl_FragColor = vec4( diffuseColor.rgb, alpha );
#include <tonemapping_fragment>
#include <encodings_fragment>
#include <fog_fragment>
#include <premultiplied_alpha_fragment>
}
`
@@ -0,0 +1,244 @@
export const speckle_line_vert = /* glsl */ `
#include <common>
#include <color_pars_vertex>
#include <fog_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>
uniform float linewidth;
uniform vec2 resolution;
uniform vec3 uViewer_high;
uniform vec3 uViewer_low;
attribute vec3 instanceStart;
attribute vec3 instanceEnd;
attribute vec3 instanceColorStart;
attribute vec3 instanceColorEnd;
attribute vec3 position_high;
attribute vec3 position_low;
#ifdef WORLD_UNITS
varying vec4 worldPos;
varying vec3 worldStart;
varying vec3 worldEnd;
#ifdef USE_DASH
varying vec2 vUv;
#endif
#else
varying vec2 vUv;
#endif
#ifdef USE_DASH
uniform float dashScale;
attribute float instanceDistanceStart;
attribute float instanceDistanceEnd;
varying float vLineDistance;
#endif
void trimSegment( const in vec4 start, inout vec4 end ) {
// trim end segment so it terminates between the camera plane and the near plane
// conservative estimate of the near plane
float a = projectionMatrix[ 2 ][ 2 ]; // 3nd entry in 3th column
float b = projectionMatrix[ 3 ][ 2 ]; // 3nd entry in 4th column
float nearEstimate = - 0.5 * b / a;
float alpha = ( nearEstimate - start.z ) / ( end.z - start.z );
end.xyz = mix( start.xyz, end.xyz, alpha );
}
void main() {
vec3 highDifference = vec3(position_high.xyz - uViewer_high);
vec3 lowDifference = vec3(position_low.xyz - uViewer_low);
vec3 computedPosition = position;//highDifference.xyz + lowDifference.xyz;
#ifdef USE_COLOR
vColor.xyz = ( computedPosition.y < 0.5 ) ? instanceColorStart : instanceColorEnd;
#endif
#ifdef USE_DASH
vLineDistance = ( computedPosition.y < 0.5 ) ? dashScale * instanceDistanceStart : dashScale * instanceDistanceEnd;
vUv = uv;
#endif
float aspect = resolution.x / resolution.y;
// camera space
vec3 startHighDifference = vec3(instanceStart.xyz - uViewer_high);
vec3 startLowDifference = vec3(instanceStart.xyz - uViewer_low);
vec3 endHighDifference = vec3(instanceEnd.xyz - uViewer_high);
vec3 endLowDifference = vec3(instanceEnd.xyz - uViewer_low);
vec4 start = modelViewMatrix * vec4( startLowDifference + startHighDifference, 1.0 );
vec4 end = modelViewMatrix * vec4( endLowDifference + endHighDifference, 1.0 );
// vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 );
// vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 );
#ifdef WORLD_UNITS
worldStart = start.xyz;
worldEnd = end.xyz;
#else
vUv = uv;
#endif
// special case for perspective projection, and segments that terminate either in, or behind, the camera plane
// clearly the gpu firmware has a way of addressing this issue when projecting into ndc space
// but we need to perform ndc-space calculations in the shader, so we must address this issue directly
// perhaps there is a more elegant solution -- WestLangley
bool perspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 ); // 4th entry in the 3rd column
if ( perspective ) {
if ( start.z < 0.0 && end.z >= 0.0 ) {
trimSegment( start, end );
} else if ( end.z < 0.0 && start.z >= 0.0 ) {
trimSegment( end, start );
}
}
// clip space
vec4 clipStart = projectionMatrix * start;
vec4 clipEnd = projectionMatrix * end;
// ndc space
vec3 ndcStart = clipStart.xyz / clipStart.w;
vec3 ndcEnd = clipEnd.xyz / clipEnd.w;
// direction
vec2 dir = ndcEnd.xy - ndcStart.xy;
// account for clip-space aspect ratio
dir.x *= aspect;
dir = normalize( dir );
#ifdef WORLD_UNITS
// get the offset direction as perpendicular to the view vector
vec3 worldDir = normalize( end.xyz - start.xyz );
vec3 offset;
if ( computedPosition.y < 0.5 ) {
offset = normalize( cross( start.xyz, worldDir ) );
} else {
offset = normalize( cross( end.xyz, worldDir ) );
}
// sign flip
if ( computedPosition.x < 0.0 ) offset *= - 1.0;
float forwardOffset = dot( worldDir, vec3( 0.0, 0.0, 1.0 ) );
// don't extend the line if we're rendering dashes because we
// won't be rendering the endcaps
#ifndef USE_DASH
// extend the line bounds to encompass endcaps
start.xyz += - worldDir * linewidth * 0.5;
end.xyz += worldDir * linewidth * 0.5;
// shift the position of the quad so it hugs the forward edge of the line
offset.xy -= dir * forwardOffset;
offset.z += 0.5;
#endif
// endcaps
if ( computedPosition.y > 1.0 || computedPosition.y < 0.0 ) {
offset.xy += dir * 2.0 * forwardOffset;
}
// adjust for linewidth
offset *= linewidth * 0.5;
// set the world position
worldPos = ( computedPosition.y < 0.5 ) ? start : end;
worldPos.xyz += offset;
// project the worldpos
vec4 clip = projectionMatrix * worldPos;
// shift the depth of the projected points so the line
// segments overlap neatly
vec3 clipPose = ( computedPosition.y < 0.5 ) ? ndcStart : ndcEnd;
clip.z = clipPose.z * clip.w;
#else
vec2 offset = vec2( dir.y, - dir.x );
// undo aspect ratio adjustment
dir.x /= aspect;
offset.x /= aspect;
// sign flip
if ( computedPosition.x < 0.0 ) offset *= - 1.0;
// endcaps
if ( computedPosition.y < 0.0 ) {
offset += - dir;
} else if ( computedPosition.y > 1.0 ) {
offset += dir;
}
// adjust for linewidth
offset *= linewidth;
// adjust for clip-space to screen-space conversion // maybe resolution should be based on viewport ...
offset /= resolution.y;
// select end
vec4 clip = ( computedPosition.y < 0.5 ) ? clipStart : clipEnd;
// back to clip space
offset *= clip.w;
clip.xy += offset;
#endif
gl_Position = clip;
vec4 mvPosition = ( computedPosition.y < 0.5 ) ? start : end; // this is an approximation
#include <logdepthbuf_vertex>
#include <clipping_planes_vertex>
#include <fog_vertex>
}
`
@@ -0,0 +1,147 @@
export const speckle_standard_frag = /* glsl */ `
#define STANDARD
#ifdef PHYSICAL
#define IOR
#define SPECULAR
#endif
uniform vec3 diffuse;
uniform vec3 emissive;
uniform float roughness;
uniform float metalness;
uniform float opacity;
#ifdef IOR
uniform float ior;
#endif
#ifdef SPECULAR
uniform float specularIntensity;
uniform vec3 specularColor;
#ifdef USE_SPECULARINTENSITYMAP
uniform sampler2D specularIntensityMap;
#endif
#ifdef USE_SPECULARCOLORMAP
uniform sampler2D specularColorMap;
#endif
#endif
#ifdef USE_CLEARCOAT
uniform float clearcoat;
uniform float clearcoatRoughness;
#endif
#ifdef USE_SHEEN
uniform vec3 sheenColor;
uniform float sheenRoughness;
#ifdef USE_SHEENCOLORMAP
uniform sampler2D sheenColorMap;
#endif
#ifdef USE_SHEENROUGHNESSMAP
uniform sampler2D sheenRoughnessMap;
#endif
#endif
varying vec3 vViewPosition;
#include <common>
#include <packing>
#include <dithering_pars_fragment>
#include <color_pars_fragment>
#include <uv_pars_fragment>
#include <uv2_pars_fragment>
#include <map_pars_fragment>
#include <alphamap_pars_fragment>
#include <alphatest_pars_fragment>
#include <aomap_pars_fragment>
#include <lightmap_pars_fragment>
#include <emissivemap_pars_fragment>
#include <bsdfs>
#include <cube_uv_reflection_fragment>
#include <envmap_common_pars_fragment>
#include <envmap_physical_pars_fragment>
#include <fog_pars_fragment>
#include <lights_pars_begin>
#include <normal_pars_fragment>
#include <lights_physical_pars_fragment>
#include <transmission_pars_fragment>
#include <shadowmap_pars_fragment>
#include <bumpmap_pars_fragment>
#include <normalmap_pars_fragment>
#include <clearcoat_pars_fragment>
#include <roughnessmap_pars_fragment>
#include <metalnessmap_pars_fragment>
#include <logdepthbuf_pars_fragment>
#include <clipping_planes_pars_fragment>
void main() {
#include <clipping_planes_fragment>
vec4 diffuseColor = vec4( diffuse, opacity );
ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
vec3 totalEmissiveRadiance = emissive;
#include <logdepthbuf_fragment>
#include <map_fragment>
#include <color_fragment>
#include <alphamap_fragment>
#include <alphatest_fragment>
#include <roughnessmap_fragment>
#include <metalnessmap_fragment>
#include <normal_fragment_begin>
#include <normal_fragment_maps>
#include <clearcoat_normal_fragment_begin>
#include <clearcoat_normal_fragment_maps>
#include <emissivemap_fragment>
// accumulation
#include <lights_physical_fragment>
#include <lights_fragment_begin>
#include <lights_fragment_maps>
#include <lights_fragment_end>
// modulation
#include <aomap_fragment>
vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;
vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular;
#include <transmission_fragment>
vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance;
#ifdef USE_SHEEN
// Sheen energy compensation approximation calculation can be found at the end of
// https://drive.google.com/file/d/1T0D1VSyR4AllqIJTQAraEIzjlb5h4FKH/view?usp=sharing
float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor );
outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular;
#endif
#ifdef USE_CLEARCOAT
float dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );
vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc );
outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat;
#endif
#include <output_fragment>
#include <tonemapping_fragment>
#include <encodings_fragment>
#include <fog_fragment>
#include <premultiplied_alpha_fragment>
#include <dithering_fragment>
}
`
@@ -0,0 +1,77 @@
export const speckle_standard_vert = /* glsl */ `
#define STANDARD
attribute vec3 position_high;
attribute vec3 position_low;
varying vec3 vViewPosition;
#ifdef USE_TRANSMISSION
varying vec3 vWorldPosition;
#endif
#include <common>
#include <uv_pars_vertex>
#include <uv2_pars_vertex>
#include <displacementmap_pars_vertex>
#include <color_pars_vertex>
#include <fog_pars_vertex>
#include <normal_pars_vertex>
#include <morphtarget_pars_vertex>
#include <skinning_pars_vertex>
#include <shadowmap_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>
uniform vec3 uViewer_high;
uniform vec3 uViewer_low;
void main() {
#include <uv_vertex>
#include <uv2_vertex>
#include <color_vertex>
#include <beginnormal_vertex>
#include <morphnormal_vertex>
#include <skinbase_vertex>
#include <skinnormal_vertex>
#include <defaultnormal_vertex>
#include <normal_vertex>
#include <begin_vertex>
#include <morphtarget_vertex>
#include <skinning_vertex>
#include <displacementmap_vertex>
//#include <project_vertex> // EDITED CHUNK
vec3 highDifference = vec3(position_high.xyz - uViewer_high);
vec3 lowDifference = vec3(position_low.xyz - uViewer_low);
vec4 mvPosition = vec4(highDifference.xyz + lowDifference.xyz , 1.);//vec4( transformed - uViewer, 1.0 );
#ifdef USE_INSTANCING
mvPosition = instanceMatrix * mvPosition;
#endif
mvPosition = modelViewMatrix * mvPosition;
gl_Position = projectionMatrix * mvPosition;
#include <logdepthbuf_vertex>
#include <clipping_planes_vertex>
vViewPosition = - mvPosition.xyz;
#include <worldpos_vertex>
#include <shadowmap_vertex>
#include <fog_vertex>
#ifdef USE_TRANSMISSION
vWorldPosition = worldPosition.xyz;
#endif
}
`