Merge pull request #777 from specklesystems/alex/viewer-redux
Alex/viewer redux
This commit is contained in:
+1
-1
@@ -39,4 +39,4 @@ events.json
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
|
||||
*yarn-error.log
|
||||
*yarn-error.log
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { Viewer } from '@speckle/viewer'
|
||||
import { Viewer, DefaultViewerParams } from '@speckle/viewer'
|
||||
import throttle from 'lodash/throttle'
|
||||
|
||||
export default {
|
||||
@@ -41,7 +41,8 @@ export default {
|
||||
renderDomElement.id = 'renderer'
|
||||
}
|
||||
if (!window.__viewer) {
|
||||
window.__viewer = new Viewer({ container: renderDomElement, showStats: false })
|
||||
window.__viewer = new Viewer(renderDomElement, DefaultViewerParams)
|
||||
await window.__viewer.init()
|
||||
}
|
||||
|
||||
this.domElement = renderDomElement
|
||||
|
||||
@@ -8,15 +8,27 @@
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<div id="renderer" />
|
||||
<!-- <div id="app">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="twelve columns h-96">
|
||||
<div id="renderer" />
|
||||
<input
|
||||
id="objectUrlInput"
|
||||
class="input"
|
||||
type="text"
|
||||
name="objectId"
|
||||
placeholder="Object Url"
|
||||
style="width: 49%"
|
||||
value="https://latest.speckle.dev/streams/010b3af4c3/objects/a401baf38fe5809d0eb9d3c902a36e8f"
|
||||
/>
|
||||
<button id="loadButton" class="button" onclick="loadData()" style="width: 29%">
|
||||
Load Object URL
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,186 @@
|
||||
import { Viewer, IViewer } from '@speckle/viewer'
|
||||
import SpeckleLineMaterial from '@speckle/viewer/dist/modules/materials/SpeckleLineMaterial'
|
||||
import { Object3D } from '@speckle/viewer/node_modules/@types/three'
|
||||
import { Pane } from 'tweakpane'
|
||||
import UrlHelper from './UrlHelper'
|
||||
export default class Sandbox {
|
||||
private viewer: IViewer
|
||||
private pane: Pane
|
||||
private tabs
|
||||
|
||||
public static urlParams = {
|
||||
url: 'https://latest.speckle.dev/streams/010b3af4c3/objects/a401baf38fe5809d0eb9d3c902a36e8f'
|
||||
}
|
||||
|
||||
public static sceneParams = {
|
||||
worldSize: { x: 0, y: 0, z: 0 },
|
||||
worldOrigin: { x: 0, y: 0, z: 0 },
|
||||
useRTE: false,
|
||||
thickLines: true,
|
||||
pixelThreshold: 0.5,
|
||||
exposure: 0.4,
|
||||
tonemapping: 'Linear'
|
||||
}
|
||||
|
||||
public constructor(viewer: Viewer) {
|
||||
this.viewer = viewer
|
||||
this.pane = new Pane({ title: 'Sandbox', expanded: true })
|
||||
this.pane['containerElem_'].style.width = '300px'
|
||||
const t = `matrix(1.2, 0, 0, 1.2, -25, 16)`
|
||||
this.pane['containerElem_'].style.transform = t
|
||||
this.tabs = this.pane.addTab({
|
||||
pages: [{ title: 'General' }, { title: 'Scene' }]
|
||||
})
|
||||
Sandbox.sceneParams.useRTE = viewer.RTE
|
||||
Sandbox.sceneParams.thickLines = viewer.thickLines
|
||||
}
|
||||
|
||||
public refresh() {
|
||||
this.pane.refresh()
|
||||
}
|
||||
|
||||
public makeGenericUI() {
|
||||
this.tabs.pages[0].addInput(Sandbox.urlParams, 'url', {
|
||||
title: 'url'
|
||||
})
|
||||
|
||||
const loadButton = this.tabs.pages[0].addButton({
|
||||
title: 'Load Url'
|
||||
})
|
||||
|
||||
loadButton.on('click', () => {
|
||||
this.loadUrl(Sandbox.urlParams.url)
|
||||
})
|
||||
|
||||
const clearButton = this.tabs.pages[0].addButton({
|
||||
title: 'Clear All'
|
||||
})
|
||||
|
||||
clearButton.on('click', () => {
|
||||
this.viewer.unloadAll()
|
||||
})
|
||||
|
||||
this.tabs.pages[0].addSeparator()
|
||||
|
||||
const toggleSectionBox = this.tabs.pages[0].addButton({
|
||||
title: 'Toggle Section Box'
|
||||
})
|
||||
toggleSectionBox.on('click', () => {
|
||||
this.viewer.toggleSectionBox()
|
||||
})
|
||||
|
||||
const toggleProjection = this.tabs.pages[0].addButton({
|
||||
title: 'Toggle Projection'
|
||||
})
|
||||
toggleProjection.on('click', () => {
|
||||
this.viewer.toggleCameraProjection()
|
||||
})
|
||||
|
||||
const zoomExtents = this.tabs.pages[0].addButton({
|
||||
title: 'Zoom Extents'
|
||||
})
|
||||
zoomExtents.on('click', () => {
|
||||
this.viewer.zoomExtents(undefined, true)
|
||||
})
|
||||
}
|
||||
|
||||
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'
|
||||
})
|
||||
|
||||
worldFolder
|
||||
.addInput(Sandbox.sceneParams, 'useRTE', {
|
||||
label: 'RTE'
|
||||
})
|
||||
.on('change', () => {
|
||||
this.viewer.RTE = Sandbox.sceneParams.useRTE
|
||||
})
|
||||
|
||||
worldFolder
|
||||
.addInput(Sandbox.sceneParams, 'thickLines', {
|
||||
label: 'Thick Lines'
|
||||
})
|
||||
.on('change', () => {
|
||||
this.viewer.thickLines = Sandbox.sceneParams.thickLines
|
||||
})
|
||||
|
||||
worldFolder
|
||||
.addInput(Sandbox.sceneParams, 'pixelThreshold', {
|
||||
min: 0,
|
||||
max: 5
|
||||
})
|
||||
.on('change', () => {
|
||||
this.viewer.scene.traverse((object: Object3D) => {
|
||||
if (object.type === 'Line2') {
|
||||
;(object.material as SpeckleLineMaterial).pixelThreshold =
|
||||
Sandbox.sceneParams.pixelThreshold
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
this.tabs.pages[1].addSeparator()
|
||||
const postFolder = this.tabs.pages[1].addFolder({
|
||||
title: 'Post',
|
||||
expanded: true
|
||||
})
|
||||
|
||||
postFolder
|
||||
.addInput(Sandbox.sceneParams, 'exposure', {
|
||||
min: 0,
|
||||
max: 1
|
||||
})
|
||||
.on('change', () => {
|
||||
this.viewer.renderer.toneMappingExposure = Sandbox.sceneParams.exposure
|
||||
})
|
||||
|
||||
postFolder
|
||||
.addInput(Sandbox.sceneParams, 'tonemapping', {
|
||||
options: {
|
||||
Linear: 1,
|
||||
ACES: 4
|
||||
}
|
||||
})
|
||||
.on('change', () => {
|
||||
this.viewer.renderer.toneMapping = Sandbox.sceneParams.tonemapping
|
||||
})
|
||||
}
|
||||
|
||||
public async loadUrl(url: string) {
|
||||
const objUrls = await UrlHelper.getResourceUrls(url)
|
||||
for (const url of objUrls) {
|
||||
console.log(`Loading ${url}`)
|
||||
await this.viewer.loadObject(url)
|
||||
}
|
||||
localStorage.setItem('last-load-url', url)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
interface CommitReferencedObjectUrl {
|
||||
origin: string
|
||||
streamId: string
|
||||
commitId: string
|
||||
}
|
||||
|
||||
export default class UrlHelper {
|
||||
static async getResourceUrls(url: string): Promise<string[]> {
|
||||
const parsed = new URL(url)
|
||||
const streamId = url.split('/streams/')[1].substring(0, 10)
|
||||
|
||||
const objsUrls = []
|
||||
// supports commit based urls
|
||||
if (url.includes('commits')) {
|
||||
const commitId = url.split('/commits/')[1].substring(0, 10)
|
||||
const objUrl = await this.getCommitReferencedObjectUrl({
|
||||
origin: parsed.origin,
|
||||
streamId,
|
||||
commitId
|
||||
})
|
||||
objsUrls.push(objUrl)
|
||||
}
|
||||
|
||||
// object based urls
|
||||
if (url.includes('objects')) objsUrls.push(url)
|
||||
|
||||
// supports urls that include overlay queries
|
||||
// e.g., https://speckle.xyz/streams/a632e7a784/objects/457c45feffa6f954572e5e86fb6d4f25?overlay=cf8dc76247,f5adc1d991b3dceb4b5ad6b50f919a0e
|
||||
if (url.includes('overlay=')) {
|
||||
const searchParams = new URLSearchParams(parsed.search)
|
||||
const resIds = searchParams.get('overlay')?.split(',')
|
||||
if (resIds !== undefined) {
|
||||
for (const resId of resIds) {
|
||||
if (resId.length === 10) {
|
||||
objsUrls.push(
|
||||
await this.getCommitReferencedObjectUrl({
|
||||
origin: parsed.origin,
|
||||
streamId,
|
||||
commitId: resId
|
||||
} as CommitReferencedObjectUrl)
|
||||
)
|
||||
} else {
|
||||
objsUrls.push(`${parsed.origin}/streams/${streamId}/objects/${resId}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return objsUrls
|
||||
}
|
||||
|
||||
private static async getCommitReferencedObjectUrl(ref: CommitReferencedObjectUrl) {
|
||||
const headers: { 'Content-Type': string; Authorization: string } = {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: ''
|
||||
}
|
||||
const authToken = localStorage.getItem('AuthToken')
|
||||
if (authToken) {
|
||||
headers['Authorization'] = `Bearer ${authToken}`
|
||||
}
|
||||
const res = await fetch(`${ref.origin}/graphql`, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify({
|
||||
query: `
|
||||
query Stream($streamId: String!, $commitId: String!) {
|
||||
stream(id: $streamId) {
|
||||
commit(id: $commitId) {
|
||||
referencedObject
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: { streamId: ref.streamId, commitId: ref.commitId }
|
||||
})
|
||||
})
|
||||
|
||||
const { data } = await res.json()
|
||||
return `${ref.origin}/streams/${ref.streamId}/objects/${data.stream.commit.referencedObject}`
|
||||
}
|
||||
}
|
||||
@@ -1,42 +1,41 @@
|
||||
import { Pane } from 'tweakpane'
|
||||
import { Viewer } from '@speckle/viewer'
|
||||
import './style.css'
|
||||
import { Viewer, DefaultViewerParams } from '@speckle/viewer'
|
||||
|
||||
const container = document.querySelector<HTMLDivElement>('#renderer')
|
||||
import './style.css'
|
||||
import Sandbox from './Sandbox'
|
||||
|
||||
const container = document.querySelector<HTMLElement>('#renderer')
|
||||
if (!container) {
|
||||
throw new Error("Couldn't find #app container!")
|
||||
}
|
||||
|
||||
// Viewer setup
|
||||
const viewer = new Viewer({
|
||||
container,
|
||||
showStats: true
|
||||
})
|
||||
const params = DefaultViewerParams
|
||||
// params.environmentSrc =
|
||||
// 'https://speckle-xyz-assets.ams3.digitaloceanspaces.com/studio010.hdr'
|
||||
// 'http://localhost:3033/sample-hdri.exr'
|
||||
|
||||
const viewer = new Viewer(container, params)
|
||||
await viewer.init()
|
||||
|
||||
const sandbox = new Sandbox(viewer)
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
viewer.onWindowResize()
|
||||
})
|
||||
|
||||
// Tweakpane setup
|
||||
const PARAMS = {
|
||||
factor: 123,
|
||||
title: 'hello',
|
||||
color: '#ff0055'
|
||||
}
|
||||
|
||||
const pane = new Pane()
|
||||
|
||||
pane.addInput(PARAMS, 'factor')
|
||||
pane.addInput(PARAMS, 'title')
|
||||
pane.addInput(PARAMS, 'color')
|
||||
|
||||
// Load demo object
|
||||
viewer.loadObject(
|
||||
'https://speckle.xyz/streams/9217731fc1/objects/111a9dc2ed245f26a6584354b11b083f'
|
||||
)
|
||||
|
||||
viewer.on<{ progress: number; id: string; url: string }>('load-progress', (a) => {
|
||||
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')
|
||||
|
||||
@@ -12,3 +12,30 @@
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.button {
|
||||
border: 0;
|
||||
line-height: 1.5;
|
||||
padding: 0 20px;
|
||||
font-size: 1rem;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
text-shadow: 1px 1px 1px #000;
|
||||
border-radius: 2px;
|
||||
background-color: rgb(129, 129, 129);
|
||||
background-image: linear-gradient(
|
||||
to top left,
|
||||
rgba(0, 0, 0, 0.2),
|
||||
rgba(0, 0, 0, 0.2) 30%,
|
||||
rgba(0, 0, 0, 0)
|
||||
);
|
||||
box-shadow: inset 2px 2px 3px rgba(255, 255, 255, 0.6),
|
||||
inset -2px -2px 3px rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
.input {
|
||||
margin-bottom: 5px;
|
||||
left: 0px;
|
||||
border-radius: 0.1rem;
|
||||
border: 4px solid rgb(129, 129, 129);
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/**
|
||||
* In the future we can integrate these types into the actual viewer package
|
||||
*/
|
||||
|
||||
declare module '@speckle/viewer' {
|
||||
declare class Viewer {
|
||||
constructor(params: {
|
||||
container: Node
|
||||
postprocessing?: boolean = false
|
||||
reflections?: boolean = true
|
||||
showStats?: boolean = false
|
||||
})
|
||||
|
||||
async loadObject(url: string, token?: string, enableCaching? = true): Promise<void>
|
||||
onWindowResize(): void
|
||||
on<A1 = unknown, A2 = unknown, A3 = unknown>(
|
||||
event: string,
|
||||
callback: (arg1: A1, arg2: A2, arg3: A3) => void
|
||||
)
|
||||
}
|
||||
export { Viewer }
|
||||
}
|
||||
Executable
+7350
File diff suppressed because one or more lines are too long
Executable
+7369
File diff suppressed because one or more lines are too long
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"useBuiltIns": "entry",
|
||||
"corejs": "3"
|
||||
}
|
||||
]
|
||||
],
|
||||
"ignore": ["node_modules/**/*"]
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
since 2019
|
||||
@@ -14,7 +14,19 @@ const config = {
|
||||
rules: {
|
||||
'no-console': ['warn', { allow: ['warn', 'error'] }]
|
||||
},
|
||||
ignorePatterns: ['dist2', 'example/speckleviewer.web.js']
|
||||
ignorePatterns: ['dist2', 'example/speckleviewer.web.js'],
|
||||
overrides: [
|
||||
{
|
||||
files: '*.ts',
|
||||
plugins: ['@typescript-eslint'],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'prettier'
|
||||
],
|
||||
parser: '@typescript-eslint/parser'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
module.exports = config
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
},
|
||||
"main": "dist/speckleviewer.js",
|
||||
"module": "dist/speckleviewer.esm.js",
|
||||
"exports": {
|
||||
".": "./dist/speckleviewer.esm.js",
|
||||
"./dist/assets/*": "./dist/assets/*",
|
||||
"./assets/*": "./dist/assets/*"
|
||||
},
|
||||
"types": "./dist/index.d.ts",
|
||||
"sourceType": "module",
|
||||
"files": [
|
||||
"dist"
|
||||
@@ -37,41 +43,29 @@
|
||||
"dependencies": {
|
||||
"@speckle/objectloader": "workspace:^",
|
||||
"camera-controls": "^1.33.1",
|
||||
"core-js": "^3.21.1",
|
||||
"hold-event": "^0.1.0",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"rainbowvis.js": "^1.0.1",
|
||||
"regenerator-runtime": "^0.13.7",
|
||||
"three": "^0.136.0"
|
||||
"three": "^0.140.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.15.7",
|
||||
"@babel/core": "7.15.8",
|
||||
"@babel/eslint-parser": "^7.15.8",
|
||||
"@babel/plugin-proposal-class-properties": "^7.14.5",
|
||||
"@babel/plugin-proposal-private-methods": "^7.14.5",
|
||||
"@babel/plugin-transform-classes": "^7.16.0",
|
||||
"@babel/preset-env": "7.15.8",
|
||||
"@babel/preset-react": "7.14.5",
|
||||
"@babel/preset-typescript": "7.15.0",
|
||||
"@rollup/plugin-babel": "^5.3.1",
|
||||
"@rollup/plugin-commonjs": "^21.0.3",
|
||||
"@rollup/plugin-node-resolve": "^13.1.3",
|
||||
"babel-jest": "27.2.5",
|
||||
"babel-loader": "^8.0.0-beta.4",
|
||||
"babel-plugin-add-module-exports": "1.0.4",
|
||||
"babel-plugin-transform-class-properties": "6.24.1",
|
||||
"browserslist": "^4.20.2",
|
||||
"@rollup/plugin-typescript": "^8.3.2",
|
||||
"@types/three": "^0.136.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.21.0",
|
||||
"@typescript-eslint/parser": "^5.21.0",
|
||||
"eslint": "^8.11.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"http-server": "^14.1.0",
|
||||
"jest": "27.2.5",
|
||||
"mocha": "^9.1.2",
|
||||
"prettier": "^2.5.1",
|
||||
"rollup": "^2.70.1",
|
||||
"rollup-plugin-delete": "^2.0.0",
|
||||
"rollup-plugin-rebase": "^4.1.1",
|
||||
"rollup-plugin-copy": "^3.4.0",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"yargs": "^17.2.1"
|
||||
"rollup-plugin-typescript2": "^0.31.2",
|
||||
"typescript": "^4.5.4"
|
||||
},
|
||||
"gitHead": "5627e490f9a3ecadf19cc4686ad15f344d9ad2d3"
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import commonjs from '@rollup/plugin-commonjs'
|
||||
import nodeResolve from '@rollup/plugin-node-resolve'
|
||||
import { terser } from 'rollup-plugin-terser'
|
||||
import { babel } from '@rollup/plugin-babel'
|
||||
import clean from 'rollup-plugin-delete'
|
||||
import pkg from './package.json'
|
||||
import typescript2 from 'rollup-plugin-typescript2'
|
||||
import rebasePlugin from 'rollup-plugin-rebase'
|
||||
import copyPlugin from 'rollup-plugin-copy'
|
||||
|
||||
const isProd = process.env.NODE_ENV === 'production'
|
||||
const isExample = !!process.env.EXAMPLE_BUILD
|
||||
@@ -36,6 +38,15 @@ function buildConfig(isWebBuild = false) {
|
||||
])
|
||||
],
|
||||
plugins: [
|
||||
rebasePlugin({ keepName: true }),
|
||||
copyPlugin({
|
||||
targets: [{ src: './always-bundled-assets/**/*', dest: 'dist/assets' }]
|
||||
}),
|
||||
typescript2({
|
||||
tsconfigOverride: {
|
||||
sourceMap: sourcemap
|
||||
}
|
||||
}),
|
||||
...(isWebBuild
|
||||
? [
|
||||
// Bundling in all deps in web build
|
||||
@@ -46,7 +57,6 @@ function buildConfig(isWebBuild = false) {
|
||||
// Cleaning dir only inside dist
|
||||
clean({ targets: 'dist/*' })
|
||||
]),
|
||||
babel({ babelHelpers: 'bundled' }),
|
||||
...(isProd ? [terser()] : [])
|
||||
],
|
||||
external: isWebBuild
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import sampleHdri from './assets/sample-hdri.png'
|
||||
|
||||
export interface ViewerParams {
|
||||
postprocessing: boolean
|
||||
reflections: boolean
|
||||
showStats: boolean
|
||||
environmentSrc: Asset | string
|
||||
}
|
||||
export enum AssetType {
|
||||
TEXTURE_8BPP = 'png', // For now
|
||||
TEXTURE_HDR = 'hdr',
|
||||
TEXTURE_EXR = 'exr'
|
||||
}
|
||||
|
||||
export interface Asset {
|
||||
src: string
|
||||
type: AssetType
|
||||
}
|
||||
|
||||
/**
|
||||
* The default HDRI the viewer uses is actually a true HDR image (.exr),
|
||||
* specified by the explicit TEXTURE_EXR
|
||||
*
|
||||
* We do this because bundling an actual .exr or .hdr image format would require
|
||||
* anybody consuming the viewer to make adjustments to their build config, to enable
|
||||
* its import.
|
||||
*
|
||||
* Three.js doesn't mind the extension of the asset you load, so an .exr hidden behind
|
||||
* a .png will work just fine.
|
||||
*/
|
||||
export const DefaultViewerParams: ViewerParams = {
|
||||
postprocessing: false,
|
||||
reflections: true,
|
||||
showStats: true,
|
||||
environmentSrc: {
|
||||
src: sampleHdri,
|
||||
type: AssetType.TEXTURE_EXR
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Carried over from the old Viewer. To be extended/changed
|
||||
*/
|
||||
export interface IViewer {
|
||||
init(): Promise<void>
|
||||
toggleSectionBox(): void
|
||||
sectionBoxOff(): void
|
||||
sectionBoxOn(): void
|
||||
zoomExtents(fit: number, transition: boolean): void
|
||||
toggleCameraProjection(): void
|
||||
|
||||
loadObject(url: string, token?: string, enableCaching?: boolean): Promise<void>
|
||||
cancelLoad(url: string, unload?: boolean): Promise<void>
|
||||
unloadObject(url: string): Promise<void>
|
||||
unloadAll(): Promise<void>
|
||||
|
||||
applyFilter(filter: unknown): Promise<unknown>
|
||||
getObjectsProperties(includeAll?: boolean): unknown
|
||||
|
||||
dispose(): void
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Speckle Viewer</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Space+Mono:ital,wght@0,400;0,700;1,700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link
|
||||
href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css"
|
||||
integrity="sha512-EZLkOqwILORob+p0BXZc+Vm3RgJBOe1Iq/0fiI7r/wJgzOFZMlsqTa29UEl6v6U6gsV4uIpsNZoV32YZqrCRCQ=="
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<style type="text/css">
|
||||
#renderer {
|
||||
height: 700px;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body style="background: #e4e4e4">
|
||||
<div class="container">
|
||||
<div class="row" style="padding-top: 20px">
|
||||
<div class="twelve columns">
|
||||
<h3>Viewer</h3>
|
||||
<h5>Controls summary:</h5>
|
||||
<p class="text-sm">
|
||||
Click an object to select it. Double click it to focus on it. Press `esc` to
|
||||
clear the selection. Press `shift-s` to toggle a section plane. Press `s`
|
||||
while the section plane is active to toggle its control mode. Double click
|
||||
anywhere outside an object to zoom extents to the entire scene.
|
||||
</p>
|
||||
</div>
|
||||
<div class="twelve columns">
|
||||
<button onclick="v.postprocessing = !v.postprocessing">
|
||||
Postprocessing Toggle
|
||||
</button>
|
||||
<button onclick="v.interactions.zoomExtents()">Zoom Extents</button>
|
||||
<button onclick="v.toggleSectionBox()">Toggle Section Box</button>
|
||||
<button onclick="v.interactions.rotateCamera(undefined, undefined, false)">
|
||||
Rotate
|
||||
</button>
|
||||
<button onclick="viewerScreenshot()">Screenshot</button>
|
||||
</div>
|
||||
<div class="twelve columns">
|
||||
<input
|
||||
id="objectUrlInput"
|
||||
type="text"
|
||||
name="objectId"
|
||||
placeholder="Object Url"
|
||||
style="width: 49%"
|
||||
value="https://latest.speckle.dev/streams/010b3af4c3/objects/a401baf38fe5809d0eb9d3c902a36e8f"
|
||||
/>
|
||||
<button class="" onclick="loadData()" style="width: 49%">
|
||||
Load Object URL
|
||||
</button>
|
||||
</div>
|
||||
<div class="twelve columns">
|
||||
<button onclick="v.unloadAll()">Dispose Everything</button>
|
||||
View:
|
||||
<button onclick="v.toggleCameraProjection()">Ortho/Perspective</button>
|
||||
<button onclick="v.interactions.rotateTo()">3D</button>
|
||||
<button onclick="v.interactions.rotateTo('top')">Top</button>
|
||||
<button onclick="v.interactions.rotateTo('front')">Front</button>
|
||||
<button onclick="v.interactions.rotateTo('back')">Back</button>
|
||||
<button onclick="v.interactions.rotateTo('left')">Left</button>
|
||||
<button onclick="v.interactions.rotateTo('right')">Right</button>
|
||||
</div>
|
||||
|
||||
<div class="twelve columns">
|
||||
Used Memory (MB):
|
||||
<span id="info-mem">-</span>
|
||||
/ LoadProgress:
|
||||
<span id="info-progress">-</span>
|
||||
/ ViewerBusy:
|
||||
<span id="info-busy">idle</span>
|
||||
/ Draw Calls:
|
||||
<span id="info-draws">-</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="twelve columns">
|
||||
<div id="renderer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
export type Maybe<T> = T | null
|
||||
export type Optional<T> = T | undefined
|
||||
@@ -1,8 +1,5 @@
|
||||
// POLYFILLS
|
||||
import 'core-js'
|
||||
import 'regenerator-runtime/runtime'
|
||||
|
||||
import Viewer from './modules/Viewer'
|
||||
import { Viewer } from './modules/Viewer'
|
||||
import Converter from './modules/converter/Converter'
|
||||
import { DefaultViewerParams } from './IViewer'
|
||||
|
||||
export { Viewer, Converter }
|
||||
export { Viewer, Converter, DefaultViewerParams }
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { Texture, PMREMGenerator, WebGLRenderer, TextureLoader } from 'three'
|
||||
import { EXRLoader } from 'three/examples/jsm/loaders/EXRLoader.js'
|
||||
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js'
|
||||
import { Asset, AssetType } from '../IViewer'
|
||||
|
||||
export class Assets {
|
||||
private _cache: { [name: string]: Texture } = {}
|
||||
private pmremGenerator: PMREMGenerator
|
||||
|
||||
public constructor(renderer: WebGLRenderer) {
|
||||
this.pmremGenerator = new PMREMGenerator(renderer)
|
||||
this.pmremGenerator.compileEquirectangularShader()
|
||||
}
|
||||
|
||||
private getLoader(src: string, assetType: AssetType): TextureLoader {
|
||||
if (assetType === undefined) assetType = src.split('.').pop() as AssetType
|
||||
if (!Object.values(AssetType).includes(assetType)) {
|
||||
console.warn(`Asset ${src} could not be loaded. Unknown type`)
|
||||
return null
|
||||
}
|
||||
switch (assetType) {
|
||||
case AssetType.TEXTURE_EXR:
|
||||
return new EXRLoader()
|
||||
case AssetType.TEXTURE_HDR:
|
||||
return new RGBELoader()
|
||||
case AssetType.TEXTURE_8BPP:
|
||||
return new TextureLoader()
|
||||
}
|
||||
}
|
||||
|
||||
public getEnvironment(asset: Asset | string): Promise<Texture> {
|
||||
let srcUrl: string = null
|
||||
let assetType: AssetType = undefined
|
||||
if ((<Asset>asset).src) {
|
||||
srcUrl = (asset as Asset).src
|
||||
assetType = (asset as Asset).type
|
||||
} else {
|
||||
srcUrl = asset as string
|
||||
}
|
||||
if (this._cache[srcUrl]) {
|
||||
return Promise.resolve(this._cache[srcUrl])
|
||||
}
|
||||
|
||||
return new Promise<Texture>((resolve, reject) => {
|
||||
const loader = this.getLoader(srcUrl, assetType)
|
||||
if (loader) {
|
||||
loader.load(
|
||||
srcUrl,
|
||||
(texture) => {
|
||||
const pmremRT = this.pmremGenerator.fromEquirectangular(texture)
|
||||
this._cache[srcUrl] = pmremRT.texture
|
||||
texture.dispose()
|
||||
resolve(this._cache[srcUrl])
|
||||
},
|
||||
undefined,
|
||||
(error: ErrorEvent) => {
|
||||
reject(`Loading asset ${srcUrl} failed ${error.message}`)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
reject(`Loading asset ${srcUrl} failed`)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,32 @@
|
||||
import * as THREE from 'three'
|
||||
import Rainbow from 'rainbowvis.js'
|
||||
import SpeckleStandardMaterial from './materials/SpeckleStandardMaterial'
|
||||
import { Geometry } from './converter/Geometry'
|
||||
|
||||
export default class FilteringManager {
|
||||
constructor(viewer) {
|
||||
this.viewer = viewer
|
||||
this.WireframeMaterial = new THREE.MeshStandardMaterial({
|
||||
color: 0x7080a0,
|
||||
side: THREE.DoubleSide,
|
||||
transparent: true,
|
||||
opacity: 0.2,
|
||||
wireframe: false
|
||||
})
|
||||
this.WireframeMaterial = new SpeckleStandardMaterial(
|
||||
{
|
||||
color: 0x7080a0,
|
||||
side: THREE.DoubleSide,
|
||||
transparent: true,
|
||||
opacity: 0.2,
|
||||
wireframe: false
|
||||
},
|
||||
Geometry.USE_RTE ? ['USE_RTE'] : undefined
|
||||
)
|
||||
// console.log(this.viewer.sectionBox.planes)
|
||||
|
||||
this.ColoredMaterial = new THREE.MeshStandardMaterial({
|
||||
color: 0x7080a0,
|
||||
side: THREE.DoubleSide,
|
||||
transparent: false,
|
||||
clippingPlanes: this.viewer.sectionBox.planes
|
||||
})
|
||||
this.ColoredMaterial = new SpeckleStandardMaterial(
|
||||
{
|
||||
color: 0x7080a0,
|
||||
side: THREE.DoubleSide,
|
||||
transparent: false,
|
||||
clippingPlanes: this.viewer.sectionBox.planes
|
||||
},
|
||||
Geometry.USE_RTE ? ['USE_RTE'] : undefined
|
||||
)
|
||||
|
||||
this.colorLegend = {}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import * as THREE from 'three'
|
||||
import SelectionHelper from './SelectionHelper'
|
||||
import { Line2 } from 'three/examples/jsm/lines/Line2.js'
|
||||
import SpeckleLambertMaterial from './materials/SpeckleLambertMaterial'
|
||||
import { Geometry } from './converter/Geometry'
|
||||
import SpeckleLineMaterial from './materials/SpeckleLineMaterial'
|
||||
|
||||
export default class InteractionHandler {
|
||||
constructor(viewer) {
|
||||
@@ -10,23 +14,39 @@ export default class InteractionHandler {
|
||||
sectionBox: this.sectionBox,
|
||||
hover: false
|
||||
})
|
||||
this.selectionMeshMaterial = new THREE.MeshLambertMaterial({
|
||||
color: 0x0b55d2,
|
||||
side: THREE.DoubleSide,
|
||||
wireframe: false,
|
||||
transparent: true,
|
||||
opacity: 0.3
|
||||
})
|
||||
this.selectionMeshMaterial = new SpeckleLambertMaterial(
|
||||
{
|
||||
color: 0x0b55d2,
|
||||
side: THREE.DoubleSide,
|
||||
wireframe: false,
|
||||
transparent: true,
|
||||
opacity: 0.3
|
||||
},
|
||||
Geometry.USE_RTE ? ['USE_RTE'] : []
|
||||
)
|
||||
this.selectionMeshMaterial.clippingPlanes = this.viewer.sectionBox.planes
|
||||
// Fix overlapping faces flickering
|
||||
this.selectionMeshMaterial.polygonOffset = true
|
||||
this.selectionMeshMaterial.polygonOffsetFactor = -0.1
|
||||
|
||||
this.selectionLineMaterial = new THREE.LineBasicMaterial({ color: 0x0b55d2 })
|
||||
this.selectionLineMaterial.clippingPlanes = this.viewer.sectionBox.planes
|
||||
// this.selectionLineMaterial = new THREE.LineBasicMaterial({ color: 0x0b55d2 })
|
||||
// this.selectionLineMaterial.clippingPlanes = this.viewer.sectionBox.planes
|
||||
|
||||
this.selectionEdgesMaterial = new THREE.LineBasicMaterial({ color: 0x23f3bd })
|
||||
this.selectionEdgesMaterial.clippingPlanes = this.viewer.sectionBox.planes
|
||||
// this.selectionEdgesMaterial = new THREE.LineBasicMaterial({ color: 0x23f3bd })
|
||||
// this.selectionEdgesMaterial.clippingPlanes = this.viewer.sectionBox.planes
|
||||
|
||||
this.selectionLine2Material = new SpeckleLineMaterial({
|
||||
color: new THREE.Color(0x0b55d2),
|
||||
linewidth: 1,
|
||||
worldUnits: false,
|
||||
vertexColors: false,
|
||||
alphaToCoverage: true,
|
||||
resolution: this.viewer.renderer.getDrawingBufferSize(new THREE.Vector2()),
|
||||
clippingPlanes: this.viewer.sectionBox.planes
|
||||
})
|
||||
// Not a fan of this, but it should be fine for now
|
||||
this.selectionLine2Material.polygonOffset = true
|
||||
this.selectionLine2Material.polygonOffsetFactor = -0.1
|
||||
|
||||
this.selectedObjects = new THREE.Group()
|
||||
this.viewer.scene.add(this.selectedObjects)
|
||||
@@ -34,13 +54,16 @@ export default class InteractionHandler {
|
||||
this.selectionBox = new THREE.Group()
|
||||
this.viewer.scene.add(this.selectionBox)
|
||||
|
||||
this.overlayMeshMaterial = new THREE.MeshLambertMaterial({
|
||||
color: 0x57f7ff,
|
||||
side: THREE.DoubleSide,
|
||||
wireframe: false,
|
||||
transparent: true,
|
||||
opacity: 0.7
|
||||
})
|
||||
this.overlayMeshMaterial = new SpeckleLambertMaterial(
|
||||
{
|
||||
color: 0x57f7ff,
|
||||
side: THREE.DoubleSide,
|
||||
wireframe: false,
|
||||
transparent: true,
|
||||
opacity: 0.7
|
||||
},
|
||||
Geometry.USE_RTE ? ['USE_RTE'] : []
|
||||
)
|
||||
this.overlayMeshMaterial.clippingPlanes = this.viewer.sectionBox.planes
|
||||
this.overlaidObjects = new THREE.Group()
|
||||
this.viewer.scene.add(this.overlaidObjects)
|
||||
@@ -146,10 +169,25 @@ export default class InteractionHandler {
|
||||
|
||||
switch (selType) {
|
||||
case 'Block': {
|
||||
/**
|
||||
* Currently lines inside a group will not highlight
|
||||
*/
|
||||
const blockObjs = this.getBlockObjectsCloned(rootBlock)
|
||||
for (const child of blockObjs) {
|
||||
child.userData = { id: rootBlock.userData.id }
|
||||
child.material = this.selectionMeshMaterial
|
||||
if (child.type === 'Line2') {
|
||||
const material = this.selectionLine2Material.clone()
|
||||
material.color = new THREE.Color(0x0b55d2) // I really don't know why I need to reassign this...
|
||||
material.linewidth = child.material.linewidth
|
||||
material.worldUnits = child.material.worldUnits
|
||||
material.alphaToCoverage = child.material.alphaToCoverage
|
||||
material.resolution = this.viewer.renderer.getDrawingBufferSize(
|
||||
new THREE.Vector2()
|
||||
)
|
||||
child.material = material
|
||||
} else {
|
||||
child.material = this.selectionMeshMaterial
|
||||
}
|
||||
this.selectedObjects.add(child)
|
||||
//this.viewer.outlinePass.selectedObjects.push( child )
|
||||
}
|
||||
@@ -169,6 +207,23 @@ export default class InteractionHandler {
|
||||
//this.viewer.outlinePass.selectedObjects.push( new THREE.Line( objs[0].object.geometry, this.selectionMeshMaterial ) )
|
||||
break
|
||||
}
|
||||
case 'Line2': {
|
||||
const material = this.selectionLine2Material.clone()
|
||||
material.color = new THREE.Color(0x0b55d2) // I really don't know why I need to reassign this...
|
||||
material.linewidth = objs[0].object.material.linewidth
|
||||
material.worldUnits = objs[0].object.material.worldUnits
|
||||
material.alphaToCoverage = objs[0].object.material.alphaToCoverage
|
||||
material.resolution = this.viewer.renderer.getDrawingBufferSize(
|
||||
new THREE.Vector2()
|
||||
)
|
||||
const l = new Line2(objs[0].object.geometry, material)
|
||||
// Geometry.updateRTEGeometry(l.geometry)
|
||||
// l.computeLineDistances()
|
||||
// l.scale.set(1, 1, 1)
|
||||
l.userData = { id: objs[0].object.userData.id }
|
||||
this.selectedObjects.add(l)
|
||||
break
|
||||
}
|
||||
case 'Point':
|
||||
console.warn('Point selection not implemented.')
|
||||
return // exit the whole func here, points cause all sorts of trouble when being selected (ie, bbox stuff)
|
||||
@@ -183,6 +238,12 @@ export default class InteractionHandler {
|
||||
}
|
||||
|
||||
const box = new THREE.Box3().setFromObject(this.selectedObjects)
|
||||
if (selType === 'Line2') {
|
||||
const expand = objs[0].object.material.worldUnits
|
||||
? objs[0].object.material.linewidth * 0.5
|
||||
: 0
|
||||
box.expandByScalar(expand)
|
||||
}
|
||||
const boxHelper = new THREE.Box3Helper(box, 0x047efb)
|
||||
this.selectionBox.clear()
|
||||
this.selectionBox.add(boxHelper)
|
||||
@@ -195,6 +256,7 @@ export default class InteractionHandler {
|
||||
location: objs[0].point,
|
||||
selectionCenter
|
||||
}
|
||||
// console.log(selectionInfo)
|
||||
this.viewer.emit('select', selectionInfo)
|
||||
}
|
||||
|
||||
@@ -214,6 +276,7 @@ export default class InteractionHandler {
|
||||
}
|
||||
for (const child of objects) {
|
||||
child.geometry = child.geometry.clone().applyMatrix4(block.matrix)
|
||||
Geometry.updateRTEGeometry(child.geometry)
|
||||
}
|
||||
return objects
|
||||
}
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import * as THREE from 'three'
|
||||
import debounce from 'lodash.debounce'
|
||||
import SceneObjects from './SceneObjects'
|
||||
|
||||
import { Line2 } from 'three/examples/jsm/lines/Line2.js'
|
||||
import { Vector2 } from 'three'
|
||||
import { Geometry } from './converter/Geometry'
|
||||
import SpeckleStandardMaterial from './materials/SpeckleStandardMaterial'
|
||||
import SpeckleLineMaterial from './materials/SpeckleLineMaterial'
|
||||
import SpeckleLineBasicMaterial from './materials/SpeckleLineBasicMaterial'
|
||||
import SpeckleBasicMaterial from './materials/SpeckleBasicMaterial'
|
||||
/**
|
||||
* Manages objects and provides some convenience methods to focus on the entire scene, or one specific object.
|
||||
*/
|
||||
@@ -13,54 +19,7 @@ export default class SceneObjectManager {
|
||||
|
||||
this.sceneObjects = new SceneObjects(viewer)
|
||||
|
||||
this.solidMaterial = new THREE.MeshStandardMaterial({
|
||||
color: 0x8d9194,
|
||||
emissive: 0x0,
|
||||
roughness: 1,
|
||||
metalness: 0,
|
||||
side: THREE.DoubleSide,
|
||||
envMap: this.viewer.cubeCamera.renderTarget.texture,
|
||||
clippingPlanes: this.viewer.sectionBox.planes
|
||||
})
|
||||
|
||||
this.transparentMaterial = new THREE.MeshStandardMaterial({
|
||||
color: 0xa0a4a8,
|
||||
emissive: 0x0,
|
||||
roughness: 0,
|
||||
metalness: 0.5,
|
||||
side: THREE.DoubleSide,
|
||||
transparent: true,
|
||||
opacity: 0.4,
|
||||
envMap: this.viewer.cubeCamera.renderTarget.texture,
|
||||
clippingPlanes: this.viewer.sectionBox.planes
|
||||
})
|
||||
|
||||
this.solidVertexMaterial = new THREE.MeshBasicMaterial({
|
||||
color: 0xffffff,
|
||||
vertexColors: THREE.VertexColors,
|
||||
side: THREE.DoubleSide,
|
||||
reflectivity: 0,
|
||||
clippingPlanes: this.viewer.sectionBox.planes
|
||||
})
|
||||
|
||||
this.lineMaterial = new THREE.LineBasicMaterial({
|
||||
color: 0x7f7f7f,
|
||||
clippingPlanes: this.viewer.sectionBox.planes
|
||||
})
|
||||
|
||||
this.pointMaterial = new THREE.PointsMaterial({
|
||||
size: 2,
|
||||
sizeAttenuation: false,
|
||||
color: 0x7f7f7f,
|
||||
clippingPlanes: this.viewer.sectionBox.planes
|
||||
})
|
||||
|
||||
this.pointVertexColorsMaterial = new THREE.PointsMaterial({
|
||||
size: 2,
|
||||
sizeAttenuation: false,
|
||||
vertexColors: true,
|
||||
clippingPlanes: this.viewer.sectionBox.planes
|
||||
})
|
||||
this.initMaterials()
|
||||
|
||||
this.postLoad = debounce(
|
||||
() => {
|
||||
@@ -102,6 +61,69 @@ export default class SceneObjectManager {
|
||||
]
|
||||
}
|
||||
|
||||
initMaterials() {
|
||||
if (this.solidMaterial) this.solidMaterial.dispose()
|
||||
if (this.transparentMaterial) this.transparentMaterial.dispose()
|
||||
if (this.solidVertexMaterial) this.solidVertexMaterial.dispose()
|
||||
if (this.lineMaterial) this.lineMaterial.dispose()
|
||||
if (this.pointMaterial) this.pointMaterial.dispose()
|
||||
if (this.pointVertexColorsMaterial) this.pointVertexColorsMaterial.dispose()
|
||||
|
||||
this.solidMaterial = new SpeckleStandardMaterial(
|
||||
{
|
||||
color: 0x8d9194,
|
||||
emissive: 0x0,
|
||||
roughness: 1,
|
||||
metalness: 0,
|
||||
side: THREE.DoubleSide,
|
||||
// envMap: this.viewer.cubeCamera.renderTarget.texture,
|
||||
clippingPlanes: this.viewer.sectionBox.planes
|
||||
},
|
||||
Geometry.USE_RTE ? ['USE_RTE'] : undefined
|
||||
)
|
||||
|
||||
this.transparentMaterial = new SpeckleStandardMaterial(
|
||||
{
|
||||
color: 0xa0a4a8,
|
||||
emissive: 0x0,
|
||||
roughness: 0,
|
||||
metalness: 0.5,
|
||||
side: THREE.DoubleSide,
|
||||
transparent: true,
|
||||
opacity: 0.4,
|
||||
// envMap: this.viewer.cubeCamera.renderTarget.texture,
|
||||
clippingPlanes: this.viewer.sectionBox.planes
|
||||
},
|
||||
Geometry.USE_RTE ? ['USE_RTE'] : undefined
|
||||
)
|
||||
|
||||
this.solidVertexMaterial = new SpeckleBasicMaterial(
|
||||
{
|
||||
color: 0xffffff,
|
||||
vertexColors: THREE.VertexColors,
|
||||
side: THREE.DoubleSide,
|
||||
reflectivity: 0,
|
||||
clippingPlanes: this.viewer.sectionBox.planes
|
||||
},
|
||||
Geometry.USE_RTE ? ['USE_RTE'] : undefined
|
||||
)
|
||||
|
||||
this.lineMaterial = this.makeLineMaterial()
|
||||
|
||||
this.pointMaterial = new THREE.PointsMaterial({
|
||||
size: 2,
|
||||
sizeAttenuation: false,
|
||||
color: 0x7f7f7f,
|
||||
clippingPlanes: this.viewer.sectionBox.planes
|
||||
})
|
||||
|
||||
this.pointVertexColorsMaterial = new THREE.PointsMaterial({
|
||||
size: 2,
|
||||
sizeAttenuation: false,
|
||||
vertexColors: true,
|
||||
clippingPlanes: this.viewer.sectionBox.planes
|
||||
})
|
||||
}
|
||||
// Note: we might switch later down the line from cloning materials to solely
|
||||
// using a few "default" ones and controlling color through vertex colors.
|
||||
// For now a small compromise to speed up dev; it is not the most memory
|
||||
@@ -142,7 +164,8 @@ export default class SceneObjectManager {
|
||||
if (wrapper.meta.renderMaterial) {
|
||||
const renderMat = wrapper.meta.renderMaterial
|
||||
const color = new THREE.Color(this._argbToRGB(renderMat.diffuse))
|
||||
this._normaliseColor(color)
|
||||
color.convertSRGBToLinear()
|
||||
// this._normaliseColor(color);
|
||||
// Is it a transparent material?
|
||||
if (renderMat.opacity !== 1) {
|
||||
const material = this.transparentMaterial.clone()
|
||||
@@ -210,11 +233,14 @@ export default class SceneObjectManager {
|
||||
let material = this.lineMaterial
|
||||
if (wrapper.meta.displayStyle) {
|
||||
material = this.lineMaterial.clone()
|
||||
// This will only add confusion since it *might* work on some platforms.
|
||||
// However, it's in pixels and the displayStyle will express the thickness in world units
|
||||
// This will be replaced by the upcoming change to line rendering which supports variable
|
||||
// thickness in both world space and pixels
|
||||
// material.linewidth = wrapper.meta.displayStyle.lineweight > 0 ? wrapper.meta.displayStyle : 1
|
||||
if (wrapper.meta.displayStyle.lineweight > 0) {
|
||||
material.linewidth = wrapper.meta.displayStyle.lineweight
|
||||
material.worldUnits = true
|
||||
material.pixelThreshold = 0.5
|
||||
} else {
|
||||
material.linewidth = 1
|
||||
material.worldUnits = false
|
||||
}
|
||||
material.color = new THREE.Color(this._argbToRGB(wrapper.meta.displayStyle.color))
|
||||
// material.color.convertSRGBToLinear();
|
||||
|
||||
@@ -227,8 +253,9 @@ export default class SceneObjectManager {
|
||||
// material.color.convertSRGBToLinear();
|
||||
material.clippingPlanes = this.viewer.sectionBox.planes
|
||||
}
|
||||
material.resolution = this.viewer.renderer.getDrawingBufferSize(new Vector2())
|
||||
|
||||
const line = new THREE.Line(wrapper.bufferGeometry, material)
|
||||
const line = this.makeLineMesh(wrapper.bufferGeometry, material)
|
||||
line.userData = wrapper.meta
|
||||
line.uuid = wrapper.meta.id
|
||||
if (addToScene) {
|
||||
@@ -256,8 +283,8 @@ export default class SceneObjectManager {
|
||||
} else if (wrapper.meta.renderMaterial) {
|
||||
const renderMat = wrapper.meta.renderMaterial
|
||||
const color = new THREE.Color(this._argbToRGB(renderMat.diffuse))
|
||||
|
||||
this._normaliseColor(color)
|
||||
color.convertSRGBToLinear()
|
||||
// this._normaliseColor(color);
|
||||
const material = this.pointMaterial.clone()
|
||||
material.clippingPlanes = this.viewer.sectionBox.planes
|
||||
// material.clippingPlanes = this.viewer.interactions.sectionBox.planes
|
||||
@@ -341,33 +368,74 @@ export default class SceneObjectManager {
|
||||
return box
|
||||
}
|
||||
|
||||
makeLineMesh(geometry, material) {
|
||||
let line
|
||||
if (Geometry.THICK_LINES) {
|
||||
line = new Line2(geometry, material)
|
||||
line.computeLineDistances()
|
||||
line.scale.set(1, 1, 1)
|
||||
} else {
|
||||
line = new THREE.Line(geometry, material)
|
||||
}
|
||||
|
||||
return line
|
||||
}
|
||||
|
||||
makeLineMaterial() {
|
||||
let lineMaterial
|
||||
if (Geometry.THICK_LINES) {
|
||||
lineMaterial = new SpeckleLineMaterial({
|
||||
color: 0x7f7f7f,
|
||||
linewidth: 1, // in world units with size attenuation, pixels otherwise
|
||||
worldUnits: false,
|
||||
vertexColors: false,
|
||||
alphaToCoverage: false,
|
||||
resolution: this.viewer.renderer.getDrawingBufferSize(new Vector2()),
|
||||
clippingPlanes: this.viewer.sectionBox.planes
|
||||
})
|
||||
} else {
|
||||
lineMaterial = new SpeckleLineBasicMaterial({
|
||||
color: 0x7f7f7f,
|
||||
clippingPlanes: this.viewer.sectionBox.planes
|
||||
})
|
||||
}
|
||||
|
||||
return lineMaterial
|
||||
}
|
||||
|
||||
_argbToRGB(argb) {
|
||||
return '#' + ('000000' + (argb & 0xffffff).toString(16)).slice(-6)
|
||||
}
|
||||
|
||||
_normaliseColor(color) {
|
||||
// Note: full of **magic numbers** that will need changing once global scene
|
||||
// is properly set up; also to test with materials coming from other software too...
|
||||
const hsl = {}
|
||||
color.getHSL(hsl)
|
||||
/**
|
||||
* This has been retired. We're using proper srbg->linear conversions now
|
||||
* @param {*} color
|
||||
* @returns
|
||||
*/
|
||||
// _normaliseColor(color) {
|
||||
// return color
|
||||
// // Note: full of **magic numbers** that will need changing once global scene
|
||||
// // is properly set up; also to test with materials coming from other software too...
|
||||
// const hsl = {}
|
||||
// color.getHSL(hsl)
|
||||
|
||||
if (hsl.s + hsl.l > 1) {
|
||||
while (hsl.s + hsl.l > 1) {
|
||||
hsl.s -= 0.05
|
||||
hsl.l -= 0.05
|
||||
}
|
||||
}
|
||||
// if (hsl.s + hsl.l > 1) {
|
||||
// while (hsl.s + hsl.l > 1) {
|
||||
// hsl.s -= 0.05
|
||||
// hsl.l -= 0.05
|
||||
// }
|
||||
// }
|
||||
|
||||
if (hsl.l > 0.6) {
|
||||
hsl.l = 0.6
|
||||
}
|
||||
// if (hsl.l > 0.6) {
|
||||
// hsl.l = 0.6
|
||||
// }
|
||||
|
||||
if (hsl.l < 0.3) {
|
||||
hsl.l = 0.3
|
||||
}
|
||||
// if (hsl.l < 0.3) {
|
||||
// hsl.l = 0.3
|
||||
// }
|
||||
|
||||
color.setHSL(hsl.h, hsl.s, hsl.l)
|
||||
}
|
||||
// color.setHSL(hsl.h, hsl.s, hsl.l)
|
||||
// }
|
||||
|
||||
_srgbToLinear(x) {
|
||||
if (x <= 0) return 0
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as THREE from 'three'
|
||||
import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils'
|
||||
import { Geometry } from './converter/Geometry'
|
||||
import FilteringManager from './FilteringManager'
|
||||
|
||||
/**
|
||||
@@ -44,6 +45,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
|
||||
@@ -257,6 +259,7 @@ export default class SceneObjects {
|
||||
for (const element of acc) {
|
||||
element.geometry = element.geometry.clone()
|
||||
element.geometry.applyMatrix4(group.matrix)
|
||||
Geometry.updateRTEGeometry(element.geometry) // TEMPORARY!!!
|
||||
}
|
||||
return acc
|
||||
}
|
||||
@@ -289,7 +292,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
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ export default class SelectionHelper extends EventEmitter {
|
||||
this.viewer = parent
|
||||
this.raycaster = new THREE.Raycaster()
|
||||
this.raycaster.params.Line.threshold = 0.1
|
||||
this.raycaster.params.Line2 = {}
|
||||
this.raycaster.params.Line2.threshold = 1
|
||||
|
||||
// optional param allows for raycasting against a subset of objects
|
||||
// this.subset = typeof _options !== 'undefined' && typeof _options.subset !== 'undefined' ? _options.subset : null;
|
||||
@@ -112,10 +114,14 @@ export default class SelectionHelper extends EventEmitter {
|
||||
normalizedPosition,
|
||||
this.viewer.cameraHandler.activeCam.camera
|
||||
)
|
||||
/**
|
||||
* This 'subset' thing is really weird and it's breaking picking. I would gladly
|
||||
* do something about it, however I'm afraid that it will open up a can of worms,
|
||||
* which are out-of-scope for now 26.05.2022
|
||||
*/
|
||||
const targetObjects = this.subset
|
||||
? this.subset
|
||||
: this.viewer.sceneManager.filteredObjects
|
||||
|
||||
let intersectedObjects = this.raycaster.intersectObjects(targetObjects)
|
||||
// filters objects in section box mode
|
||||
if (this.viewer.sectionBox.display.visible && this.checkForSectionBoxInclusion) {
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
import Stats from 'three/examples/jsm/libs/stats.module'
|
||||
|
||||
import ObjectManager from './SceneObjectManager'
|
||||
import ViewerObjectLoader from './ViewerObjectLoader'
|
||||
import EventEmitter from './EventEmitter'
|
||||
import InteractionHandler from './InteractionHandler'
|
||||
import CameraHandler from './context/CameraHanlder'
|
||||
|
||||
import SectionBox from './SectionBox'
|
||||
import { Clock, CubeCamera, Texture, 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'
|
||||
import { Geometry } from './converter/Geometry'
|
||||
|
||||
export class Viewer extends EventEmitter implements IViewer {
|
||||
private clock: Clock
|
||||
private container: HTMLElement
|
||||
private cubeCamera: CubeCamera
|
||||
private stats: Optional<Stats>
|
||||
private loaders: { [id: string]: ViewerObjectLoader } = {}
|
||||
private needsRender: boolean
|
||||
private inProgressOperations: number
|
||||
|
||||
public scene: Scene
|
||||
public sectionBox: SectionBox
|
||||
public sceneManager: ObjectManager
|
||||
public interactions: InteractionHandler
|
||||
private renderer: WebGLRenderer
|
||||
public cameraHandler: CameraHandler
|
||||
private sceneURL = '' // Temporary
|
||||
private startupParams: ViewerParams
|
||||
|
||||
public static Assets: Assets
|
||||
|
||||
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 get RTE(): boolean {
|
||||
return Geometry.USE_RTE
|
||||
}
|
||||
|
||||
public set RTE(value: boolean) {
|
||||
;(async () => {
|
||||
await this.unloadAll()
|
||||
Geometry.USE_RTE = value
|
||||
this.sceneManager.initMaterials()
|
||||
World.resetWorld()
|
||||
await this.loadObject(this.sceneURL, undefined, undefined)
|
||||
})()
|
||||
}
|
||||
|
||||
public get thickLines(): boolean {
|
||||
return Geometry.THICK_LINES
|
||||
}
|
||||
|
||||
public set thickLines(value: boolean) {
|
||||
;(async () => {
|
||||
await this.unloadAll()
|
||||
Geometry.THICK_LINES = value
|
||||
this.sceneManager.initMaterials()
|
||||
World.resetWorld()
|
||||
await this.loadObject(this.sceneURL, undefined, undefined)
|
||||
})()
|
||||
}
|
||||
|
||||
public constructor(
|
||||
container: HTMLElement,
|
||||
params: ViewerParams = DefaultViewerParams
|
||||
) {
|
||||
super()
|
||||
|
||||
window.THREE = THREE
|
||||
this.startupParams = params
|
||||
this.clock = new THREE.Clock()
|
||||
|
||||
this.container = container || document.getElementById('renderer')
|
||||
this.scene = new THREE.Scene()
|
||||
|
||||
this.renderer = new THREE.WebGLRenderer({
|
||||
antialias: true,
|
||||
alpha: true,
|
||||
preserveDrawingBuffer: true
|
||||
})
|
||||
this.renderer.setClearColor(0xcccccc, 0)
|
||||
this.renderer.setPixelRatio(window.devicePixelRatio)
|
||||
this.renderer.outputEncoding = THREE.sRGBEncoding
|
||||
this.renderer.toneMapping = THREE.LinearToneMapping
|
||||
this.renderer.toneMappingExposure = 0.5
|
||||
this.renderer.setSize(this.container.offsetWidth, this.container.offsetHeight)
|
||||
this.container.appendChild(this.renderer.domElement)
|
||||
|
||||
Viewer.Assets = new Assets(this.renderer)
|
||||
|
||||
this.cameraHandler = new CameraHandler(this)
|
||||
|
||||
const cubeRenderTarget = new THREE.WebGLCubeRenderTarget(512, {
|
||||
format: THREE.RGBFormat,
|
||||
generateMipmaps: true,
|
||||
minFilter: THREE.LinearMipmapLinearFilter
|
||||
})
|
||||
this.cubeCamera = new THREE.CubeCamera(0.1, 10_000, cubeRenderTarget)
|
||||
this.scene.add(this.cubeCamera)
|
||||
|
||||
if (params.showStats) {
|
||||
this.stats = Stats()
|
||||
this.container.appendChild(this.stats.dom)
|
||||
}
|
||||
|
||||
window.addEventListener('resize', this.onWindowResize.bind(this), false)
|
||||
|
||||
this.loaders = {}
|
||||
|
||||
this.sectionBox = new SectionBox(this)
|
||||
this.sectionBox.off()
|
||||
|
||||
this.sceneManager = new ObjectManager(this)
|
||||
this.interactions = new InteractionHandler(this)
|
||||
|
||||
this.sceneLights()
|
||||
this.animate()
|
||||
this.onWindowResize()
|
||||
this.interactions.zoomExtents()
|
||||
this.needsRender = true
|
||||
|
||||
this.inProgressOperations = 0
|
||||
}
|
||||
|
||||
public async init(): Promise<void> {
|
||||
if (this.startupParams.environmentSrc) {
|
||||
Viewer.Assets.getEnvironment(this.startupParams.environmentSrc)
|
||||
.then((value: Texture) => {
|
||||
this.scene.environment = value
|
||||
})
|
||||
.catch((reason) => {
|
||||
console.warn(reason)
|
||||
console.warn('Fallback to null environment!')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private sceneLights() {
|
||||
// const dirLight = new THREE.DirectionalLight( 0xffffff, 0.1 )
|
||||
// dirLight.color.setHSL( 0.1, 1, 0.95 )
|
||||
// dirLight.position.set( -1, 1.75, 1 )
|
||||
// dirLight.position.multiplyScalar( 1000 )
|
||||
// this.scene.add( dirLight )
|
||||
|
||||
// const dirLight2 = new THREE.DirectionalLight( 0xffffff, 0.9 )
|
||||
// dirLight2.color.setHSL( 0.1, 1, 0.95 )
|
||||
// dirLight2.position.set( 0, -1.75, 1 )
|
||||
// dirLight2.position.multiplyScalar( 1000 )
|
||||
// this.scene.add( dirLight2 )
|
||||
|
||||
// const hemiLight2 = new THREE.HemisphereLight( 0xffffff, new THREE.Color( '#232323' ), 1.9 )
|
||||
// hemiLight2.color.setHSL( 1, 1, 1 )
|
||||
// // hemiLight2.groundColor = new THREE.Color( '#232323' )
|
||||
// hemiLight2.up.set( 0, 0, 1 )
|
||||
// this.scene.add( hemiLight2 )
|
||||
|
||||
// let axesHelper = new THREE.AxesHelper( 1 )
|
||||
// this.scene.add( axesHelper )
|
||||
|
||||
// return
|
||||
|
||||
const ambientLight = new THREE.AmbientLight(0xffffff)
|
||||
this.scene.add(ambientLight)
|
||||
|
||||
const lights = []
|
||||
lights[0] = new THREE.PointLight(0xffffff, 0.21, 0)
|
||||
lights[1] = new THREE.PointLight(0xffffff, 0.21, 0)
|
||||
lights[2] = new THREE.PointLight(0xffffff, 0.21, 0)
|
||||
lights[3] = new THREE.PointLight(0xffffff, 0.21, 0)
|
||||
|
||||
const factor = 1000
|
||||
lights[0].position.set(1 * factor, 1 * factor, 1 * factor)
|
||||
lights[1].position.set(1 * factor, -1 * factor, 1 * factor)
|
||||
lights[2].position.set(-1 * factor, -1 * factor, 1 * factor)
|
||||
lights[3].position.set(-1 * factor, 1 * factor, 1 * factor)
|
||||
|
||||
this.scene.add(lights[0])
|
||||
this.scene.add(lights[1])
|
||||
this.scene.add(lights[2])
|
||||
this.scene.add(lights[3])
|
||||
|
||||
// let sphereSize = 0.2
|
||||
// this.scene.add( new THREE.PointLightHelper( lights[ 0 ], sphereSize ) )
|
||||
// this.scene.add( new THREE.PointLightHelper( lights[ 1 ], sphereSize ) )
|
||||
// this.scene.add( new THREE.PointLightHelper( lights[ 2 ], sphereSize ) )
|
||||
// this.scene.add( new THREE.PointLightHelper( lights[ 3 ], sphereSize ) )
|
||||
|
||||
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x0, 0.2)
|
||||
hemiLight.color.setHSL(1, 1, 1)
|
||||
hemiLight.groundColor.setHSL(0.095, 1, 0.75)
|
||||
hemiLight.up.set(0, 0, 1)
|
||||
this.scene.add(hemiLight)
|
||||
|
||||
const group = new THREE.Group()
|
||||
this.scene.add(group)
|
||||
}
|
||||
|
||||
onWindowResize() {
|
||||
this.renderer.setSize(this.container.offsetWidth, this.container.offsetHeight)
|
||||
this.needsRender = true
|
||||
}
|
||||
|
||||
private animate() {
|
||||
const delta = this.clock.getDelta()
|
||||
|
||||
const hasControlsUpdated = this.cameraHandler.controls.update(delta)
|
||||
|
||||
requestAnimationFrame(this.animate.bind(this))
|
||||
|
||||
// you can skip this condition to render though
|
||||
if (hasControlsUpdated || this.needsRender) {
|
||||
this.needsRender = false
|
||||
if (this.stats) this.stats.begin()
|
||||
this.render()
|
||||
|
||||
const infoDrawsEl = document.getElementById('info-draws')
|
||||
if (this.stats && infoDrawsEl) {
|
||||
infoDrawsEl.textContent = '' + this.renderer.info.render.calls
|
||||
}
|
||||
if (this.stats) this.stats.end()
|
||||
}
|
||||
}
|
||||
|
||||
private render() {
|
||||
this.renderer.render(this.scene, this.cameraHandler.activeCam.camera)
|
||||
}
|
||||
|
||||
public toggleSectionBox() {
|
||||
this.sectionBox.toggle()
|
||||
}
|
||||
|
||||
public sectionBoxOff() {
|
||||
this.sectionBox.off()
|
||||
}
|
||||
|
||||
public sectionBoxOn() {
|
||||
this.sectionBox.on()
|
||||
}
|
||||
|
||||
public zoomExtents(fit?: number, transition?: boolean) {
|
||||
this.interactions.zoomExtents(fit, transition)
|
||||
}
|
||||
|
||||
public setProjectionMode(mode: typeof CameraHandler.prototype.activeCam) {
|
||||
this.cameraHandler.activeCam = mode
|
||||
}
|
||||
|
||||
public toggleCameraProjection() {
|
||||
this.cameraHandler.toggleCameras()
|
||||
}
|
||||
|
||||
public async loadObject(
|
||||
url: string,
|
||||
token: string | undefined,
|
||||
enableCaching = true
|
||||
) {
|
||||
try {
|
||||
if (++this.inProgressOperations === 1) (this as EventEmitter).emit('busy', true)
|
||||
|
||||
const loader = new ViewerObjectLoader(this, url, token, enableCaching)
|
||||
this.loaders[url] = loader
|
||||
await loader.load()
|
||||
} finally {
|
||||
if (--this.inProgressOperations === 0) (this as EventEmitter).emit('busy', false)
|
||||
this.sceneURL = url
|
||||
}
|
||||
}
|
||||
|
||||
public async cancelLoad(url: string, unload = false) {
|
||||
this.loaders[url].cancelLoad()
|
||||
if (unload) {
|
||||
await this.unloadObject(url)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
public async unloadObject(url: string) {
|
||||
try {
|
||||
if (++this.inProgressOperations === 1) (this as EventEmitter).emit('busy', true)
|
||||
|
||||
await this.loaders[url].unload()
|
||||
delete this.loaders[url]
|
||||
} finally {
|
||||
if (--this.inProgressOperations === 0) (this as EventEmitter).emit('busy', false)
|
||||
}
|
||||
}
|
||||
|
||||
public async unloadAll() {
|
||||
for (const key of Object.keys(this.loaders)) {
|
||||
await this.loaders[key].unload()
|
||||
delete this.loaders[key]
|
||||
}
|
||||
await this.applyFilter(null)
|
||||
return
|
||||
}
|
||||
|
||||
public async applyFilter(filter: unknown) {
|
||||
try {
|
||||
if (++this.inProgressOperations === 1) (this as EventEmitter).emit('busy', true)
|
||||
|
||||
this.interactions.deselectObjects()
|
||||
return await this.sceneManager.sceneObjects.applyFilter(filter)
|
||||
} finally {
|
||||
if (--this.inProgressOperations === 0) (this as EventEmitter).emit('busy', false)
|
||||
}
|
||||
}
|
||||
|
||||
public getObjectsProperties(includeAll = true) {
|
||||
return this.sceneManager.sceneObjects.getObjectsProperties(includeAll)
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
// TODO: currently it's easier to simply refresh the page :)
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,7 @@ export default class ViewerObjectLoader {
|
||||
let total = 0
|
||||
let viewerLoads = 0
|
||||
let firstObjectPromise = null
|
||||
const parsedObjects = [] // Temporary until refactor
|
||||
for await (const obj of this.loader.getObjectIterator()) {
|
||||
if (this.cancel) {
|
||||
this.viewer.emit('load-progress', {
|
||||
@@ -79,12 +80,13 @@ export default class ViewerObjectLoader {
|
||||
}
|
||||
await this.converter.asyncPause()
|
||||
if (first) {
|
||||
// console.log(obj)
|
||||
firstObjectPromise = this.converter.traverseAndConvert(
|
||||
obj,
|
||||
async (objectWrapper) => {
|
||||
await this.converter.asyncPause()
|
||||
objectWrapper.meta.__importedUrl = this.objectUrl
|
||||
this.viewer.sceneManager.addObject(objectWrapper)
|
||||
parsedObjects.push(objectWrapper) // Temporary until refactor
|
||||
viewerLoads++
|
||||
}
|
||||
)
|
||||
@@ -102,7 +104,14 @@ export default class ViewerObjectLoader {
|
||||
await firstObjectPromise
|
||||
}
|
||||
|
||||
// Geometry.applyWorldTransform(parsedObjects)
|
||||
// Temporary until refactor
|
||||
for (let 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}`)
|
||||
|
||||
@@ -10,7 +10,7 @@ import CameraHandler from './context/CameraHanlder'
|
||||
|
||||
import SectionBox from './SectionBox'
|
||||
|
||||
export default class Viewer extends EventEmitter {
|
||||
export default class Viewer_old extends EventEmitter {
|
||||
constructor({
|
||||
container,
|
||||
postprocessing = false,
|
||||
@@ -0,0 +1,29 @@
|
||||
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 (let k = 0; k < this.boxes.length; k++) {
|
||||
World.worldBox.union(World.boxes[k])
|
||||
}
|
||||
}
|
||||
|
||||
public static resetWorld() {
|
||||
World.worldBox.makeEmpty()
|
||||
this.boxes.length = 0
|
||||
}
|
||||
}
|
||||
+396
-255
@@ -1,20 +1,73 @@
|
||||
import * as THREE from 'three'
|
||||
import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils'
|
||||
// import { BufferGeometryUtils } from 'three/examples/jsm/utils/BufferGeometryUtils'
|
||||
|
||||
import ObjectWrapper from './ObjectWrapper'
|
||||
import { getConversionFactor } from './Units'
|
||||
import MeshTriangulationHelper from './MeshTriangulationHelper'
|
||||
import { Matrix4 } from 'three'
|
||||
import { Geometry, GeometryData } from './Geometry'
|
||||
import { BoxBufferGeometry, EllipseCurve, Matrix4, Vector2 } from 'three'
|
||||
import { Vector3 } from 'three'
|
||||
import { Line3 } from 'three'
|
||||
|
||||
export type ConverterResultDelegate = (
|
||||
object: ObjectWrapper | undefined
|
||||
) => Promise<void>
|
||||
export type ConverterGeometryDelegate = (
|
||||
object,
|
||||
scale?: boolean
|
||||
) => Promise<ObjectWrapper | undefined>
|
||||
export type ConverterGeometryDataDelegate = (
|
||||
object,
|
||||
scale?: boolean
|
||||
) => Promise<GeometryData>
|
||||
|
||||
/**
|
||||
* Utility class providing some top level conversion methods.
|
||||
* Warning: HIC SVNT DRACONES.
|
||||
*/
|
||||
export default class Coverter {
|
||||
constructor(objectLoader) {
|
||||
private objectLoader
|
||||
private curveSegmentLength: number
|
||||
private lastAsyncPause: number
|
||||
private activePromises: number
|
||||
private maxChildrenPromises: number
|
||||
|
||||
private readonly GeometryConverterMapping: {
|
||||
[name: string]: ConverterGeometryDelegate
|
||||
} = {
|
||||
View3D: this.View3DToBufferGeometry.bind(this),
|
||||
BlockInstance: this.BlockInstanceToBufferGeometry.bind(this),
|
||||
Pointcloud: this.PointcloudToBufferGeometry.bind(this),
|
||||
Brep: this.BrepToBufferGeometry.bind(this),
|
||||
Mesh: this.MeshToBufferGeometry.bind(this),
|
||||
Point: this.PointToBufferGeometry.bind(this),
|
||||
Line: this.LineToBufferGeometry.bind(this),
|
||||
Polyline: this.PolylineToBufferGeometry.bind(this),
|
||||
Box: this.BoxToBufferGeometry.bind(this),
|
||||
Polycurve: this.PolycurveToBufferGeometry.bind(this),
|
||||
Curve: this.CurveToBufferGeometry.bind(this),
|
||||
Circle: this.CircleToBufferGeometry.bind(this),
|
||||
Arc: this.ArcToBufferGeometry.bind(this),
|
||||
Ellipse: this.EllipseToBufferGeometry.bind(this)
|
||||
}
|
||||
|
||||
private readonly GeometryDataConverterMapping: {
|
||||
[name: string]: ConverterGeometryDataDelegate
|
||||
} = {
|
||||
View3D: this.View3DToBufferGeometry.bind(this),
|
||||
BlockInstance: this.BlockInstanceToBufferGeometry.bind(this),
|
||||
Pointcloud: this.PointcloudToGeometryData.bind(this),
|
||||
Brep: this.MeshToGeometryData.bind(this),
|
||||
Mesh: this.MeshToGeometryData.bind(this),
|
||||
Point: this.PointToGeometryData.bind(this),
|
||||
Line: this.LineToGeometryData.bind(this),
|
||||
Polyline: this.PolylineToGeometryData.bind(this),
|
||||
Box: this.BoxToGeometryData.bind(this),
|
||||
Polycurve: this.PolycurveToGeometryData.bind(this),
|
||||
Curve: this.PolylineToGeometryData.bind(this),
|
||||
Circle: this.CircleToGeometryData.bind(this),
|
||||
Arc: this.ArcToGeometryData.bind(this),
|
||||
Ellipse: this.EllipseToGeometryData.bind(this)
|
||||
}
|
||||
|
||||
constructor(objectLoader: unknown) {
|
||||
if (!objectLoader) {
|
||||
console.warn(
|
||||
'Converter initialized without a corresponding object loader. Any objects that include references will throw errors.'
|
||||
@@ -29,7 +82,7 @@ export default class Coverter {
|
||||
this.maxChildrenPromises = 200
|
||||
}
|
||||
|
||||
async asyncPause() {
|
||||
private async asyncPause() {
|
||||
// Don't freeze the UI when doing all those traversals
|
||||
if (Date.now() - this.lastAsyncPause >= 100) {
|
||||
this.lastAsyncPause = Date.now()
|
||||
@@ -44,7 +97,12 @@ export default class Coverter {
|
||||
* @param {Function} callback [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
async traverseAndConvert(obj, callback, scale = true, parents = []) {
|
||||
public async traverseAndConvert(
|
||||
obj,
|
||||
callback: ConverterResultDelegate,
|
||||
scale = true,
|
||||
parents: [] = []
|
||||
) {
|
||||
await this.asyncPause()
|
||||
|
||||
// Exit on primitives (string, ints, bools, bigints, etc.)
|
||||
@@ -81,9 +139,9 @@ export default class Coverter {
|
||||
// If we can convert it, we should invoke the respective conversion routine.
|
||||
const type = this.getSpeckleType(obj)
|
||||
|
||||
if (this[`${type}ToBufferGeometry`]) {
|
||||
if (this.directConverterExists(obj)) {
|
||||
try {
|
||||
await callback(await this[`${type}ToBufferGeometry`](obj.data || obj, scale))
|
||||
await callback(await this.directConvert(obj.data || obj, scale))
|
||||
return
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
@@ -109,9 +167,9 @@ export default class Coverter {
|
||||
const convertedElement = await this.convert(displayValue, scale)
|
||||
await callback(
|
||||
new ObjectWrapper(
|
||||
convertedElement.bufferGeometry,
|
||||
convertedElement?.bufferGeometry,
|
||||
obj,
|
||||
convertedElement.geometryType
|
||||
convertedElement?.geometryType
|
||||
)
|
||||
) // use the parent's metadata!
|
||||
} catch (e) {
|
||||
@@ -126,9 +184,9 @@ export default class Coverter {
|
||||
const convertedElement = await this.convert(val, scale)
|
||||
await callback(
|
||||
new ObjectWrapper(
|
||||
convertedElement.bufferGeometry,
|
||||
convertedElement?.bufferGeometry,
|
||||
{ renderMaterial: val.renderMaterial, ...obj },
|
||||
convertedElement.geometryType
|
||||
convertedElement?.geometryType
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -176,11 +234,19 @@ export default class Coverter {
|
||||
this.activePromises -= childrenConversionPromisses.length
|
||||
}
|
||||
|
||||
directConverterExists(obj) {
|
||||
return this[`${this.getSpeckleType(obj)}ToBufferGeometry`] !== undefined
|
||||
private directConverterExists(obj) {
|
||||
return this.getSpeckleType(obj) in this.GeometryConverterMapping
|
||||
}
|
||||
|
||||
getDisplayValue(obj) {
|
||||
private directConvert(obj, scale = true): Promise<ObjectWrapper | undefined> {
|
||||
return this.GeometryConverterMapping[this.getSpeckleType(obj)](obj, scale)
|
||||
}
|
||||
|
||||
private convertToGeometryData(obj, scale = true): Promise<GeometryData> {
|
||||
return this.GeometryDataConverterMapping[this.getSpeckleType(obj)](obj, scale)
|
||||
}
|
||||
|
||||
private getDisplayValue(obj) {
|
||||
return (
|
||||
obj['displayValue'] ||
|
||||
obj['@displayValue'] ||
|
||||
@@ -196,24 +262,12 @@ export default class Coverter {
|
||||
* @param {Function} callback [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
async convert(obj, scale = true) {
|
||||
private async convert(obj, scale = true) {
|
||||
if (obj.referencedId) obj = await this.resolveReference(obj)
|
||||
try {
|
||||
const type = this.getSpeckleType(obj)
|
||||
if (this[`${type}ToBufferGeometry`]) {
|
||||
return await this[`${type}ToBufferGeometry`](obj.data || obj, scale)
|
||||
if (this.directConverterExists(obj)) {
|
||||
return await this.directConvert(obj.data || obj, scale)
|
||||
}
|
||||
/**
|
||||
* Regarding #723. This would be more generic and possibly handle other
|
||||
* types with missing direct convertor, however I don't feel it is the
|
||||
* 'convert' fuction's place to handle this...
|
||||
*/
|
||||
// else {
|
||||
// let element;
|
||||
// if((element = this.getDisplayValue(obj)) !== undefined) {
|
||||
// return await this.convert(element, scale);
|
||||
// }
|
||||
// }
|
||||
return null
|
||||
} catch (e) {
|
||||
console.warn(`(Direct convert) Failed to convert object with id: ${obj.id}`)
|
||||
@@ -226,7 +280,7 @@ export default class Coverter {
|
||||
* @param {[type]} arr [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
async dechunk(arr) {
|
||||
private async dechunk(arr) {
|
||||
if (!arr || arr.length === 0) return arr
|
||||
// Handles pre-chunking objects, or arrs that have not been chunked
|
||||
if (!arr[0].referencedId) return arr
|
||||
@@ -248,7 +302,7 @@ export default class Coverter {
|
||||
* @param {[type]} obj [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
async resolveReference(obj) {
|
||||
private async resolveReference(obj) {
|
||||
if (obj.referencedId) {
|
||||
const resolvedObj = await this.objectLoader.getObject(obj.referencedId)
|
||||
// this.asyncPause()
|
||||
@@ -261,7 +315,7 @@ export default class Coverter {
|
||||
* @param {[type]} obj [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
getSpeckleType(obj) {
|
||||
private getSpeckleType(obj): string {
|
||||
let type = 'Base'
|
||||
if (obj.data)
|
||||
type = obj.data.speckle_type
|
||||
@@ -271,7 +325,10 @@ export default class Coverter {
|
||||
return type
|
||||
}
|
||||
|
||||
async View3DToBufferGeometry(obj) {
|
||||
/**
|
||||
* VIEW 3D
|
||||
*/
|
||||
private async View3DToBufferGeometry(obj) {
|
||||
obj.origin.units = obj.units
|
||||
obj.target.units = obj.units
|
||||
const origin = this.PointToVector3(obj.origin)
|
||||
@@ -281,13 +338,22 @@ export default class Coverter {
|
||||
return new ObjectWrapper(obj, obj, 'View')
|
||||
}
|
||||
|
||||
async BlockInstanceToBufferGeometry(obj, scale) {
|
||||
/**
|
||||
* BLOCK INSTANCE
|
||||
*/
|
||||
private async BlockInstanceToBufferGeometry(obj, scale?: boolean) {
|
||||
const cF = scale ? getConversionFactor(obj.units) : 1
|
||||
const definition = await this.resolveReference(obj.blockDefinition)
|
||||
|
||||
const matrix = new THREE.Matrix4().set(
|
||||
...(Array.isArray(obj.transform) ? obj.transform : obj.transform.value)
|
||||
)
|
||||
/**
|
||||
* Speckle matrices are row major. Three's 'fromArray' function assumes
|
||||
* the matrix is in column major. That's why we transpose it here.
|
||||
*/
|
||||
const matrixData: number[] = Array.isArray(obj.transform)
|
||||
? obj.transform
|
||||
: obj.transform.value
|
||||
const matrix = new Matrix4().fromArray(matrixData).transpose()
|
||||
|
||||
const geoms = []
|
||||
for (const obj of definition.geometry) {
|
||||
// Note: we are passing scale = false to the conversion of all objects, as scaling *needs* to happen
|
||||
@@ -301,60 +367,52 @@ export default class Coverter {
|
||||
|
||||
return new ObjectWrapper(geoms, obj, 'block', {
|
||||
transformMatrix: matrix,
|
||||
scaleMatrix: new THREE.Matrix4().makeScale(cF, cF, cF)
|
||||
scaleMatrix: new Matrix4().makeScale(cF, cF, cF)
|
||||
})
|
||||
}
|
||||
|
||||
async PointcloudToBufferGeometry(obj, scale = true) {
|
||||
/**
|
||||
* POINT CLOUD
|
||||
*/
|
||||
private async PointcloudToGeometryData(obj, scale = true) {
|
||||
const conversionFactor = scale ? getConversionFactor(obj.units) : 1
|
||||
const buffer = new THREE.BufferGeometry()
|
||||
|
||||
const vertices = await this.dechunk(obj.points)
|
||||
|
||||
buffer.setAttribute(
|
||||
'position',
|
||||
new THREE.Float32BufferAttribute(
|
||||
!scale || conversionFactor === 1
|
||||
? vertices
|
||||
: vertices.map((v) => v * conversionFactor),
|
||||
3
|
||||
)
|
||||
)
|
||||
|
||||
const colorsRaw = await this.dechunk(obj.colors)
|
||||
let colors = null
|
||||
|
||||
if (colorsRaw && colorsRaw.length !== 0) {
|
||||
if (colorsRaw.length !== buffer.attributes.position.count) {
|
||||
if (colorsRaw.length !== vertices.length / 3) {
|
||||
console.warn(
|
||||
`Mesh (id ${obj.id}) colours are mismatched with vertice counts. The number of colours must equal the number of vertices.`
|
||||
)
|
||||
}
|
||||
|
||||
buffer.setAttribute(
|
||||
'color',
|
||||
new THREE.BufferAttribute(
|
||||
new Float32Array(buffer.attributes.position.count * 3),
|
||||
3
|
||||
)
|
||||
)
|
||||
|
||||
for (let i = 0; i < buffer.attributes.position.count; i++) {
|
||||
const color = colorsRaw[i]
|
||||
const r = (color >> 16) & 0xff
|
||||
const g = (color >> 8) & 0xff
|
||||
const b = color & 0xff
|
||||
buffer.attributes.color.setXYZ(i, r / 255, g / 255, b / 255)
|
||||
}
|
||||
colors = Geometry.unpackColors(colorsRaw)
|
||||
}
|
||||
|
||||
// delete obj.points
|
||||
// delete obj.colors
|
||||
// delete obj.sizes // note, these might be used in the future
|
||||
|
||||
return new ObjectWrapper(buffer, obj, 'pointcloud')
|
||||
return {
|
||||
attributes: {
|
||||
POSITION: vertices,
|
||||
COLOR: colors
|
||||
},
|
||||
bakeTransform: scale
|
||||
? new Matrix4().makeScale(conversionFactor, conversionFactor, conversionFactor)
|
||||
: null,
|
||||
transform: null
|
||||
} as GeometryData
|
||||
}
|
||||
|
||||
async BrepToBufferGeometry(obj, scale = true) {
|
||||
private async PointcloudToBufferGeometry(obj, scale = true) {
|
||||
return new ObjectWrapper(
|
||||
Geometry.makePointCloudGeometry(await this.PointcloudToGeometryData(obj, scale)),
|
||||
obj,
|
||||
'pointcloud'
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* BREP
|
||||
*/
|
||||
private async BrepToBufferGeometry(obj, scale = true) {
|
||||
try {
|
||||
if (!obj) return
|
||||
|
||||
@@ -367,8 +425,6 @@ export default class Coverter {
|
||||
)
|
||||
|
||||
// deletes known unneeded fields
|
||||
// delete obj.displayMesh
|
||||
// delete obj.displayValue
|
||||
delete obj.Edges
|
||||
delete obj.Faces
|
||||
delete obj.Loops
|
||||
@@ -385,149 +441,177 @@ export default class Coverter {
|
||||
}
|
||||
}
|
||||
|
||||
async MeshToBufferGeometry(obj, scale = true) {
|
||||
try {
|
||||
if (!obj) return
|
||||
/**
|
||||
* MESH
|
||||
*/
|
||||
private async MeshToGeometryData(obj, scale = true): Promise<GeometryData> {
|
||||
if (!obj) return
|
||||
|
||||
const conversionFactor = getConversionFactor(obj.units)
|
||||
const buffer = new THREE.BufferGeometry()
|
||||
const indices = []
|
||||
const conversionFactor = getConversionFactor(obj.units)
|
||||
// const buffer = new BufferGeometry()
|
||||
const indices = []
|
||||
|
||||
if (!obj.vertices) return
|
||||
if (!obj.faces) return
|
||||
if (!obj.vertices) return
|
||||
if (!obj.faces) return
|
||||
|
||||
const vertices = await this.dechunk(obj.vertices)
|
||||
const faces = await this.dechunk(obj.faces)
|
||||
const vertices = await this.dechunk(obj.vertices)
|
||||
const faces = await this.dechunk(obj.faces)
|
||||
const colorsRaw = await this.dechunk(obj.colors)
|
||||
let colors = null
|
||||
|
||||
let k = 0
|
||||
while (k < faces.length) {
|
||||
let n = faces[k]
|
||||
if (n <= 3) n += 3 // 0 -> 3, 1 -> 4
|
||||
let k = 0
|
||||
while (k < faces.length) {
|
||||
let n = faces[k]
|
||||
if (n <= 3) n += 3 // 0 -> 3, 1 -> 4
|
||||
|
||||
if (n === 3) {
|
||||
// Triangle face
|
||||
indices.push(faces[k + 1], faces[k + 2], faces[k + 3])
|
||||
} else {
|
||||
// Quad or N-gon face
|
||||
const triangulation = MeshTriangulationHelper.triangulateFace(
|
||||
k,
|
||||
faces,
|
||||
vertices
|
||||
)
|
||||
indices.push(...triangulation)
|
||||
}
|
||||
|
||||
k += n + 1
|
||||
}
|
||||
|
||||
if (vertices.length >= 65535 || indices.length >= 65535) {
|
||||
buffer.setIndex(new THREE.Uint32BufferAttribute(indices, 1))
|
||||
if (n === 3) {
|
||||
// Triangle face
|
||||
indices.push(faces[k + 1], faces[k + 2], faces[k + 3])
|
||||
} else {
|
||||
buffer.setIndex(new THREE.Uint16BufferAttribute(indices, 1))
|
||||
// Quad or N-gon face
|
||||
const triangulation = MeshTriangulationHelper.triangulateFace(
|
||||
k,
|
||||
faces,
|
||||
vertices
|
||||
)
|
||||
indices.push(
|
||||
...triangulation.filter((el) => {
|
||||
return el !== undefined
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
buffer.setAttribute(
|
||||
'position',
|
||||
new THREE.Float32BufferAttribute(
|
||||
!scale || conversionFactor === 1
|
||||
? vertices
|
||||
: vertices.map((v) => v * conversionFactor),
|
||||
3
|
||||
k += n + 1
|
||||
}
|
||||
|
||||
if (colorsRaw && colorsRaw.length !== 0) {
|
||||
if (colorsRaw.length !== vertices.length / 3) {
|
||||
console.warn(
|
||||
`Mesh (id ${obj.id}) colours are mismatched with vertice counts. The number of colours must equal the number of vertices.`
|
||||
)
|
||||
}
|
||||
colors = Geometry.unpackColors(colorsRaw)
|
||||
}
|
||||
|
||||
return {
|
||||
attributes: {
|
||||
POSITION: vertices,
|
||||
INDEX: indices,
|
||||
COLOR: colors
|
||||
},
|
||||
bakeTransform: scale
|
||||
? new Matrix4().makeScale(conversionFactor, conversionFactor, conversionFactor)
|
||||
: null,
|
||||
transform: null
|
||||
} as GeometryData
|
||||
}
|
||||
|
||||
private async MeshToBufferGeometry(obj, scale = true) {
|
||||
try {
|
||||
return new ObjectWrapper(
|
||||
Geometry.makeMeshGeometry(await this.MeshToGeometryData(obj, scale)),
|
||||
obj
|
||||
)
|
||||
|
||||
const colorsRaw = await this.dechunk(obj.colors)
|
||||
|
||||
if (colorsRaw && colorsRaw.length !== 0) {
|
||||
if (colorsRaw.length !== buffer.attributes.position.count) {
|
||||
console.warn(
|
||||
`Mesh (id ${obj.id}) colours are mismatched with vertice counts. The number of colours must equal the number of vertices.`
|
||||
)
|
||||
}
|
||||
|
||||
buffer.setAttribute(
|
||||
'color',
|
||||
new THREE.BufferAttribute(
|
||||
new Float32Array(buffer.attributes.position.count * 3),
|
||||
3
|
||||
)
|
||||
)
|
||||
|
||||
for (let i = 0; i < buffer.attributes.position.count; i++) {
|
||||
const color = colorsRaw[i]
|
||||
const r = (color >> 16) & 0xff
|
||||
const g = (color >> 8) & 0xff
|
||||
const b = color & 0xff
|
||||
buffer.attributes.color.setXYZ(i, r / 255, g / 255, b / 255)
|
||||
}
|
||||
}
|
||||
|
||||
buffer.computeVertexNormals()
|
||||
//buffer.computeFaceNormals( )
|
||||
buffer.computeBoundingSphere()
|
||||
|
||||
// delete obj.vertices
|
||||
// delete obj.faces
|
||||
// delete obj.colors
|
||||
|
||||
return new ObjectWrapper(buffer, obj)
|
||||
} catch (e) {
|
||||
console.warn(`Failed to convert mesh with id: ${obj.id}`)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
async PointToBufferGeometry(obj, scale = true) {
|
||||
const v = this.PointToVector3(obj, scale)
|
||||
const buf = new THREE.BufferGeometry().setFromPoints([v])
|
||||
|
||||
return new ObjectWrapper(buf, obj, 'point')
|
||||
/**
|
||||
* POINT
|
||||
*/
|
||||
private async PointToGeometryData(obj, scale = true): Promise<GeometryData> {
|
||||
const conversionFactor = scale ? getConversionFactor(obj.units) : 1
|
||||
return {
|
||||
attributes: {
|
||||
POSITION: this.PointToFloatArray(obj)
|
||||
},
|
||||
bakeTransform: scale
|
||||
? new Matrix4().makeScale(conversionFactor, conversionFactor, conversionFactor)
|
||||
: null,
|
||||
transform: null
|
||||
} as GeometryData
|
||||
}
|
||||
|
||||
async LineToBufferGeometry(object, scale = true) {
|
||||
if (object.value) {
|
||||
//Old line format, treat as polyline
|
||||
return this.PolylineToBufferGeometry(object, scale)
|
||||
}
|
||||
const obj = {}
|
||||
Object.assign(obj, object)
|
||||
private async PointToBufferGeometry(obj, scale = true) {
|
||||
return new ObjectWrapper(
|
||||
Geometry.makePointGeometry(await this.PointToGeometryData(obj, scale)),
|
||||
obj,
|
||||
'point'
|
||||
)
|
||||
}
|
||||
|
||||
const geometry = new THREE.BufferGeometry().setFromPoints([
|
||||
this.PointToVector3(obj.start, scale),
|
||||
this.PointToVector3(obj.end, scale)
|
||||
])
|
||||
/**
|
||||
* LINE
|
||||
*/
|
||||
private async LineToGeometryData(obj, scale = true): Promise<GeometryData> {
|
||||
const conversionFactor = scale ? getConversionFactor(obj.units) : 1
|
||||
return {
|
||||
attributes: {
|
||||
POSITION: this.PointToFloatArray(obj.start).concat(
|
||||
this.PointToFloatArray(obj.end)
|
||||
)
|
||||
},
|
||||
bakeTransform: scale
|
||||
? new Matrix4().makeScale(conversionFactor, conversionFactor, conversionFactor)
|
||||
: null,
|
||||
transform: null
|
||||
} as GeometryData
|
||||
}
|
||||
|
||||
private async LineToBufferGeometry(obj, scale = true) {
|
||||
if (obj.value) {
|
||||
//Old line format, treat as polyline
|
||||
return this.PolylineToBufferGeometry(obj, scale)
|
||||
}
|
||||
|
||||
const geometry = Geometry.makeLineGeometry(
|
||||
await this.LineToGeometryData(obj, scale)
|
||||
)
|
||||
return new ObjectWrapper(geometry, obj, 'line')
|
||||
}
|
||||
|
||||
async PolylineToBufferGeometry(object, scale = true) {
|
||||
const obj = {}
|
||||
/**
|
||||
* POLYLINE
|
||||
*/
|
||||
private async PolylineToGeometryData(object, scale = true): Promise<GeometryData> {
|
||||
const obj = Object.create({})
|
||||
Object.assign(obj, object)
|
||||
|
||||
const conversionFactor = scale ? getConversionFactor(obj.units) : 1
|
||||
|
||||
obj.value = await this.dechunk(obj.value)
|
||||
|
||||
const points = []
|
||||
for (let i = 0; i < obj.value.length; i += 3) {
|
||||
points.push(
|
||||
new THREE.Vector3(
|
||||
obj.value[i] * conversionFactor,
|
||||
obj.value[i + 1] * conversionFactor,
|
||||
obj.value[i + 2] * conversionFactor
|
||||
)
|
||||
)
|
||||
}
|
||||
if (obj.closed) points.push(points[0])
|
||||
if (obj.closed) obj.value.push(obj.value[0], obj.value[1], obj.value[2])
|
||||
return {
|
||||
attributes: {
|
||||
POSITION: obj.value
|
||||
},
|
||||
bakeTransform: scale
|
||||
? new Matrix4().makeScale(conversionFactor, conversionFactor, conversionFactor)
|
||||
: null,
|
||||
transform: null
|
||||
} as GeometryData
|
||||
}
|
||||
|
||||
const geometry = new THREE.BufferGeometry().setFromPoints(points)
|
||||
|
||||
delete obj.value
|
||||
delete obj.bbox
|
||||
async PolylineToBufferGeometry(obj, scale = true) {
|
||||
const geometry = Geometry.makeLineGeometry(
|
||||
await this.PolylineToGeometryData(obj, scale)
|
||||
)
|
||||
|
||||
return new ObjectWrapper(geometry, obj, 'line')
|
||||
}
|
||||
|
||||
async BoxToBufferGeometry(object, scale = true) {
|
||||
/**
|
||||
* BOX
|
||||
*/
|
||||
private async BoxToGeometryData(object, scale = true) {
|
||||
/**
|
||||
* Right, so we're cheating here a bit. We're using three's box geometry
|
||||
* to get the vertices and indices. Normally we could(should) do that by hand
|
||||
* but it's too late in the evenning atm...
|
||||
*/
|
||||
const conversionFactor = scale ? getConversionFactor(object.units) : 1
|
||||
|
||||
const move = this.PointToVector3(object.basePlane.origin)
|
||||
@@ -535,33 +619,57 @@ export default class Coverter {
|
||||
const depth = (object.ySize.end - object.ySize.start) * conversionFactor
|
||||
const height = (object.zSize.end - object.zSize.start) * conversionFactor
|
||||
|
||||
const box = new THREE.BoxBufferGeometry(width, depth, height, 1, 1, 1)
|
||||
box.applyMatrix4(new THREE.Matrix4().setPosition(move))
|
||||
|
||||
return new ObjectWrapper(box, object)
|
||||
const box = new BoxBufferGeometry(width, depth, height, 1, 1, 1)
|
||||
return {
|
||||
attributes: {
|
||||
POSITION: box.attributes.position.array,
|
||||
INDEX: box.index.array
|
||||
},
|
||||
bakeTransform: new Matrix4().setPosition(move),
|
||||
transform: null
|
||||
} as GeometryData
|
||||
}
|
||||
async BoxToBufferGeometry(object, scale = true) {
|
||||
return new ObjectWrapper(
|
||||
Geometry.makeMeshGeometry(await this.BoxToGeometryData(object, scale)),
|
||||
object
|
||||
)
|
||||
}
|
||||
|
||||
async PolycurveToBufferGeometry(object, scale = true) {
|
||||
const obj = {}
|
||||
/**
|
||||
* POLYCURVE
|
||||
*/
|
||||
async PolycurveToGeometryData(object, scale = true): Promise<GeometryData> {
|
||||
const obj = Object.create({})
|
||||
Object.assign(obj, object)
|
||||
|
||||
const buffers = []
|
||||
for (let i = 0; i < obj.segments.length; i++) {
|
||||
let element = obj.segments[i]
|
||||
let conv
|
||||
if (this.directConverterExists(element)) conv = await this.convert(element, scale)
|
||||
if (this.directConverterExists(element))
|
||||
conv = await this.convertToGeometryData(element, scale)
|
||||
else if ((element = this.getDisplayValue(element)) !== undefined)
|
||||
conv = await this.convert(element, scale)
|
||||
conv = await this.convertToGeometryData(element, scale)
|
||||
|
||||
buffers.push(conv?.bufferGeometry)
|
||||
buffers.push(conv)
|
||||
}
|
||||
const geometry = BufferGeometryUtils.mergeBufferGeometries(buffers)
|
||||
|
||||
return new ObjectWrapper(geometry, obj, 'line')
|
||||
return Geometry.mergeGeometryData(buffers)
|
||||
}
|
||||
|
||||
async PolycurveToBufferGeometry(object, scale = true) {
|
||||
const geometryData: GeometryData = await this.PolycurveToGeometryData(object, scale)
|
||||
const geometry = Geometry.makeLineGeometry(geometryData)
|
||||
|
||||
return new ObjectWrapper(geometry, Object.assign({}, object), 'line')
|
||||
}
|
||||
|
||||
/**
|
||||
* CURVE
|
||||
*/
|
||||
async CurveToBufferGeometry(object, scale = true) {
|
||||
const obj = {}
|
||||
const obj = Object.create({})
|
||||
Object.assign(obj, object)
|
||||
const displayValue = await this.resolveReference(obj.displayValue)
|
||||
displayValue.units = displayValue.units || obj.units
|
||||
@@ -571,44 +679,31 @@ export default class Coverter {
|
||||
return new ObjectWrapper(poly.bufferGeometry, obj, 'line')
|
||||
}
|
||||
|
||||
async CircleToBufferGeometry(obj, scale = true) {
|
||||
/**
|
||||
* CIRCLE
|
||||
*/
|
||||
async CircleToGeometryData(obj, scale = true) {
|
||||
const conversionFactor = scale ? getConversionFactor(obj.units) : 1
|
||||
const points = this.getCircularCurvePoints(obj.plane, obj.radius * conversionFactor)
|
||||
const geometry = new THREE.BufferGeometry().setFromPoints(points)
|
||||
|
||||
// delete obj.plane
|
||||
// delete obj.value
|
||||
// delete obj.speckle_type
|
||||
// delete obj.bbox
|
||||
|
||||
return {
|
||||
attributes: {
|
||||
POSITION: this.FlattenVector3Array(points)
|
||||
},
|
||||
bakeTransform: null,
|
||||
transform: null
|
||||
} as GeometryData
|
||||
}
|
||||
async CircleToBufferGeometry(obj, scale = true) {
|
||||
const geometry = Geometry.makeLineGeometry(
|
||||
await this.CircleToGeometryData(obj, scale)
|
||||
)
|
||||
return new ObjectWrapper(geometry, obj, 'line')
|
||||
}
|
||||
|
||||
async ArcToBufferGeometry(obj, scale = true) {
|
||||
/**
|
||||
* Old implementation
|
||||
*/
|
||||
// const radius = obj.radius
|
||||
// const curve = new THREE.EllipseCurve(
|
||||
// 0,
|
||||
// 0, // ax, aY
|
||||
// radius,
|
||||
// radius, // xRadius, yRadius
|
||||
// obj.startAngle,
|
||||
// obj.endAngle, // aStartAngle, aEndAngle
|
||||
// false, // aClockwise
|
||||
// 0 // aRotation
|
||||
// )
|
||||
// const points = curve.getPoints(50);
|
||||
// const t = this.PlaneToMatrix4(obj.plane, scale);
|
||||
// const geometry = new THREE.BufferGeometry()
|
||||
// .setFromPoints(points)
|
||||
// .applyMatrix4(t)
|
||||
// return new ObjectWrapper(geometry, obj, 'line')
|
||||
|
||||
/**
|
||||
* New implementation, a bit verbose, but it's more clear this way.
|
||||
*/
|
||||
/**
|
||||
* ARC
|
||||
*/
|
||||
async ArcToGeometryData(obj, scale = true) {
|
||||
const origin = new Vector3(
|
||||
obj.plane.origin.x,
|
||||
obj.plane.origin.y,
|
||||
@@ -649,7 +744,7 @@ export default class Coverter {
|
||||
const angle = Math.acos(dot)
|
||||
const radius = obj.radius
|
||||
// We draw the arc in a local un-rotated coordinate system. We rotate it later on via transformation
|
||||
const curve = new THREE.EllipseCurve(
|
||||
const curve = new EllipseCurve(
|
||||
0,
|
||||
0, // ax, aY
|
||||
radius,
|
||||
@@ -674,32 +769,43 @@ export default class Coverter {
|
||||
|
||||
if (scale) {
|
||||
const S = new Matrix4().scale(
|
||||
new THREE.Vector3(conversionFactor, conversionFactor, conversionFactor)
|
||||
new Vector3(conversionFactor, conversionFactor, conversionFactor)
|
||||
)
|
||||
matrix.multiply(S)
|
||||
}
|
||||
|
||||
const geometry = new THREE.BufferGeometry()
|
||||
.setFromPoints(points)
|
||||
.applyMatrix4(matrix)
|
||||
return {
|
||||
attributes: {
|
||||
POSITION: this.FlattenVector3Array(points)
|
||||
},
|
||||
bakeTransform: matrix,
|
||||
transform: null
|
||||
} as GeometryData
|
||||
}
|
||||
|
||||
async ArcToBufferGeometry(obj, scale = true) {
|
||||
const geometry = Geometry.makeLineGeometry(await this.ArcToGeometryData(obj, scale))
|
||||
|
||||
return new ObjectWrapper(geometry, obj, 'line')
|
||||
}
|
||||
|
||||
async EllipseToBufferGeometry(obj, scale = true) {
|
||||
/**
|
||||
* ELLIPSE
|
||||
*/
|
||||
async EllipseToGeometryData(obj, scale = true) {
|
||||
const conversionFactor = scale ? getConversionFactor(obj.units) : 1
|
||||
|
||||
const center = new THREE.Vector3(
|
||||
const center = new Vector3(
|
||||
obj.plane.origin.x,
|
||||
obj.plane.origin.y,
|
||||
obj.plane.origin.z
|
||||
).multiplyScalar(conversionFactor)
|
||||
const xAxis = new THREE.Vector3(
|
||||
const xAxis = new Vector3(
|
||||
obj.plane.xdir.x,
|
||||
obj.plane.xdir.y,
|
||||
obj.plane.xdir.z
|
||||
).normalize()
|
||||
const yAxis = new THREE.Vector3(
|
||||
const yAxis = new Vector3(
|
||||
obj.plane.ydir.x,
|
||||
obj.plane.ydir.y,
|
||||
obj.plane.ydir.z
|
||||
@@ -713,19 +819,34 @@ export default class Coverter {
|
||||
const t = (index * Math.PI * 2) / resolution
|
||||
const x = Math.cos(t) * obj.firstRadius * conversionFactor
|
||||
const y = Math.sin(t) * obj.secondRadius * conversionFactor
|
||||
const xMove = new THREE.Vector3(xAxis.x * x, xAxis.y * x, xAxis.z * x)
|
||||
const yMove = new THREE.Vector3(yAxis.x * y, yAxis.y * y, yAxis.z * y)
|
||||
const xMove = new Vector3(xAxis.x * x, xAxis.y * x, xAxis.z * x)
|
||||
const yMove = new Vector3(yAxis.x * y, yAxis.y * y, yAxis.z * y)
|
||||
|
||||
const pt = new THREE.Vector3().addVectors(xMove, yMove).add(center)
|
||||
const pt = new Vector3().addVectors(xMove, yMove).add(center)
|
||||
points.push(pt)
|
||||
}
|
||||
|
||||
const geometry = new THREE.BufferGeometry().setFromPoints(points)
|
||||
return {
|
||||
attributes: {
|
||||
POSITION: this.FlattenVector3Array(points)
|
||||
},
|
||||
bakeTransform: null,
|
||||
transform: null
|
||||
} as GeometryData
|
||||
}
|
||||
|
||||
async EllipseToBufferGeometry(obj, scale = true) {
|
||||
const geometry = Geometry.makeLineGeometry(
|
||||
await this.EllipseToGeometryData(obj, scale)
|
||||
)
|
||||
return new ObjectWrapper(geometry, obj, 'line')
|
||||
}
|
||||
|
||||
/**
|
||||
* UTILS
|
||||
*/
|
||||
PlaneToMatrix4(plane, scale = true) {
|
||||
const m = new THREE.Matrix4()
|
||||
const m = new Matrix4()
|
||||
const conversionFactor = scale ? getConversionFactor(plane.units) : 1
|
||||
|
||||
m.makeBasis(
|
||||
@@ -738,7 +859,7 @@ export default class Coverter {
|
||||
* I think scaling should be done first.
|
||||
*/
|
||||
if (scale) {
|
||||
m.scale(new THREE.Vector3(conversionFactor, conversionFactor, conversionFactor))
|
||||
m.scale(new Vector3(conversionFactor, conversionFactor, conversionFactor))
|
||||
}
|
||||
return m
|
||||
}
|
||||
@@ -769,10 +890,10 @@ export default class Coverter {
|
||||
const t = startAngle + (index * (endAngle - startAngle)) / resolution
|
||||
const x = Math.cos(t) * radius
|
||||
const y = Math.sin(t) * radius
|
||||
const xMove = new THREE.Vector3(xAxis.x * x, xAxis.y * x, xAxis.z * x)
|
||||
const yMove = new THREE.Vector3(yAxis.x * y, yAxis.y * y, yAxis.z * y)
|
||||
const xMove = new Vector3(xAxis.x * x, xAxis.y * x, xAxis.z * x)
|
||||
const yMove = new Vector3(yAxis.x * y, yAxis.y * y, yAxis.z * y)
|
||||
|
||||
const pt = new THREE.Vector3().addVectors(xMove, yMove).add(center)
|
||||
const pt = new Vector3().addVectors(xMove, yMove).add(center)
|
||||
points.push(pt)
|
||||
}
|
||||
return points
|
||||
@@ -783,14 +904,14 @@ export default class Coverter {
|
||||
let v = null
|
||||
if (obj.value) {
|
||||
// Old point format based on value list
|
||||
v = new THREE.Vector3(
|
||||
v = new Vector3(
|
||||
obj.value[0] * conversionFactor,
|
||||
obj.value[1] * conversionFactor,
|
||||
obj.value[2] * conversionFactor
|
||||
)
|
||||
} else {
|
||||
// New point format based on cartesian coords
|
||||
v = new THREE.Vector3(
|
||||
v = new Vector3(
|
||||
obj.x * conversionFactor,
|
||||
obj.y * conversionFactor,
|
||||
obj.z * conversionFactor
|
||||
@@ -798,4 +919,24 @@ export default class Coverter {
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
PointToFloatArray(obj) {
|
||||
if (obj.value) {
|
||||
return [obj.value[0], obj.value[1], obj.value[2]]
|
||||
} else {
|
||||
return [obj.x, obj.y, obj.z]
|
||||
}
|
||||
}
|
||||
|
||||
FlattenVector3Array(input: Vector3[] | Vector2[]): number[] {
|
||||
const output = new Array(input.length * 3)
|
||||
const vBuff = []
|
||||
for (let k = 0, l = 0; k < input.length; k++, l += 3) {
|
||||
input[k].toArray(vBuff)
|
||||
output[l] = vBuff[0]
|
||||
output[l + 1] = vBuff[1]
|
||||
output[l + 2] = vBuff[2] ? vBuff[2] : 0
|
||||
}
|
||||
return output
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,426 @@
|
||||
/* eslint-disable camelcase */
|
||||
import {
|
||||
Box3,
|
||||
BufferGeometry,
|
||||
Float32BufferAttribute,
|
||||
InstancedInterleavedBuffer,
|
||||
InterleavedBufferAttribute,
|
||||
Matrix4,
|
||||
Uint16BufferAttribute,
|
||||
Uint32BufferAttribute,
|
||||
Vector3
|
||||
} from 'three'
|
||||
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js'
|
||||
import { World } from '../World'
|
||||
import ObjectWrapper from './ObjectWrapper'
|
||||
|
||||
export enum GeometryAttributes {
|
||||
POSITION = 'POSITION',
|
||||
COLOR = 'COLOR',
|
||||
NORMAL = 'NORMAL',
|
||||
UV = 'UV',
|
||||
TANGENTS = 'TANGENTS',
|
||||
INDEX = 'INDEX'
|
||||
}
|
||||
|
||||
export interface GeometryData {
|
||||
attributes: Partial<Record<GeometryAttributes, number[]>>
|
||||
bakeTransform: Matrix4
|
||||
transform: Matrix4
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation here will change once we start working on proper batching
|
||||
*/
|
||||
export class Geometry {
|
||||
private static _USE_RTE = true
|
||||
private static _THICK_LINES = true
|
||||
static get USE_RTE(): boolean {
|
||||
return Geometry._USE_RTE
|
||||
}
|
||||
|
||||
static set USE_RTE(value: boolean) {
|
||||
Geometry._USE_RTE = value
|
||||
console.warn(`RTE RENDERING IS NOW ${Geometry._USE_RTE}`)
|
||||
}
|
||||
|
||||
static get THICK_LINES(): boolean {
|
||||
return Geometry._THICK_LINES
|
||||
}
|
||||
|
||||
static set THICK_LINES(value: boolean) {
|
||||
Geometry._THICK_LINES = value
|
||||
console.warn(`THICK_LINES IS NOW ${Geometry._THICK_LINES}`)
|
||||
}
|
||||
|
||||
static makePointGeometry(geometryData: GeometryData): BufferGeometry {
|
||||
const geometry = Geometry.makeMeshGeometry(geometryData)
|
||||
World.expandWorld(geometry.boundingBox)
|
||||
return geometry
|
||||
}
|
||||
static makePointCloudGeometry(geometryData: GeometryData): BufferGeometry {
|
||||
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 geometry = new BufferGeometry()
|
||||
|
||||
if (geometryData.attributes.INDEX) {
|
||||
if (
|
||||
geometryData.attributes.POSITION.length >= 65535 ||
|
||||
geometryData.attributes.INDEX.length >= 65535
|
||||
) {
|
||||
geometry.setIndex(new Uint32BufferAttribute(geometryData.attributes.INDEX, 1))
|
||||
} else {
|
||||
geometry.setIndex(new Uint16BufferAttribute(geometryData.attributes.INDEX, 1))
|
||||
}
|
||||
}
|
||||
|
||||
if (geometryData.attributes.POSITION) {
|
||||
geometry.setAttribute(
|
||||
'position',
|
||||
new Float32BufferAttribute(geometryData.attributes.POSITION, 3)
|
||||
)
|
||||
}
|
||||
|
||||
if (geometryData.attributes.COLOR) {
|
||||
geometry.setAttribute(
|
||||
'color',
|
||||
new Float32BufferAttribute(geometryData.attributes.COLOR, 3)
|
||||
)
|
||||
}
|
||||
|
||||
geometry.computeVertexNormals()
|
||||
geometry.computeBoundingSphere()
|
||||
geometry.computeBoundingBox()
|
||||
|
||||
World.expandWorld(geometry.boundingBox)
|
||||
|
||||
if (Geometry.USE_RTE) {
|
||||
Geometry.updateRTEGeometry(geometry)
|
||||
}
|
||||
|
||||
return geometry
|
||||
}
|
||||
|
||||
static makeLineGeometry(geometryData: GeometryData) {
|
||||
if (geometryData.bakeTransform) {
|
||||
Geometry.transformGeometryData(geometryData, geometryData.bakeTransform)
|
||||
}
|
||||
let geometry: { boundingBox: Box3 }
|
||||
if (Geometry.THICK_LINES) {
|
||||
geometry = this.makeLineGeometryTriangle(geometryData)
|
||||
} else {
|
||||
geometry = this.makeLineGeometryLine(geometryData)
|
||||
}
|
||||
World.expandWorld(geometry.boundingBox)
|
||||
|
||||
return geometry
|
||||
}
|
||||
|
||||
static makeLineGeometryLine(geometryData: GeometryData) {
|
||||
const geometry = new BufferGeometry()
|
||||
if (geometryData.attributes.POSITION) {
|
||||
geometry.setAttribute(
|
||||
'position',
|
||||
new Float32BufferAttribute(geometryData.attributes.POSITION, 3)
|
||||
)
|
||||
}
|
||||
geometry.computeBoundingBox()
|
||||
if (Geometry.USE_RTE) {
|
||||
Geometry.updateRTEGeometry(geometry)
|
||||
}
|
||||
|
||||
return geometry
|
||||
}
|
||||
|
||||
static makeLineGeometryTriangle(geometryData: GeometryData) {
|
||||
const geometry = new LineGeometry()
|
||||
geometry.setPositions(geometryData.attributes.POSITION)
|
||||
if (geometryData.attributes.COLOR) geometry.setColors(geometryData.attributes.COLOR)
|
||||
geometry.computeBoundingBox()
|
||||
|
||||
if (Geometry.USE_RTE) {
|
||||
Geometry.updateRTEGeometry(geometry)
|
||||
}
|
||||
return geometry
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param geometry TEMPORARY!!!
|
||||
*/
|
||||
public static updateRTEGeometry(geometry: BufferGeometry) {
|
||||
if (Geometry.USE_RTE) {
|
||||
if (geometry.type === 'BufferGeometry') {
|
||||
const position_low = new Float32Array(geometry.attributes.position.array.length)
|
||||
const position_high = new Float32Array(
|
||||
geometry.attributes.position.array.length
|
||||
)
|
||||
Geometry.DoubleToHighLowBuffer(
|
||||
geometry.attributes.position.array,
|
||||
position_low,
|
||||
position_high
|
||||
)
|
||||
geometry.setAttribute(
|
||||
'position_low',
|
||||
new Float32BufferAttribute(position_low, 3)
|
||||
)
|
||||
geometry.setAttribute(
|
||||
'position_high',
|
||||
new Float32BufferAttribute(position_high, 3)
|
||||
)
|
||||
} else if (geometry.type === 'LineGeometry') {
|
||||
const position_low = new Float32Array(
|
||||
geometry.attributes.instanceStart.array.length
|
||||
)
|
||||
const position_high = new Float32Array(
|
||||
geometry.attributes.instanceStart.array.length
|
||||
)
|
||||
|
||||
Geometry.DoubleToHighLowBuffer(
|
||||
geometry.attributes.instanceStart.array,
|
||||
position_low,
|
||||
position_high
|
||||
)
|
||||
|
||||
const instanceBufferLow = new InstancedInterleavedBuffer(
|
||||
new Float32Array(position_low),
|
||||
6,
|
||||
1
|
||||
) // xyz, xyz
|
||||
geometry.setAttribute(
|
||||
'instanceStartLow',
|
||||
new InterleavedBufferAttribute(instanceBufferLow, 3, 0)
|
||||
) // xyz
|
||||
geometry.setAttribute(
|
||||
'instanceEndLow',
|
||||
new InterleavedBufferAttribute(instanceBufferLow, 3, 3)
|
||||
) // xyz
|
||||
|
||||
const instanceBufferHigh = new InstancedInterleavedBuffer(
|
||||
new Float32Array(position_high),
|
||||
6,
|
||||
1
|
||||
) // xyz, xyz
|
||||
geometry.setAttribute(
|
||||
'instanceStartHigh',
|
||||
new InterleavedBufferAttribute(instanceBufferHigh, 3, 0)
|
||||
) // xyz
|
||||
geometry.setAttribute(
|
||||
'instanceEndHigh',
|
||||
new InterleavedBufferAttribute(instanceBufferHigh, 3, 3)
|
||||
) // xyz
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static mergeGeometryAttribute(
|
||||
attributes: number[][],
|
||||
target: Float32Array
|
||||
): ArrayLike<number> {
|
||||
let offset = 0
|
||||
for (let k = 0; k < attributes.length; k++) {
|
||||
target.set(attributes[k], offset)
|
||||
offset += attributes[k].length
|
||||
}
|
||||
return target
|
||||
}
|
||||
|
||||
static mergeIndexAttribute(
|
||||
indexAttributes: number[][],
|
||||
positionAttributes: number[][]
|
||||
): number[] {
|
||||
let indexOffset = 0
|
||||
const mergedIndex = []
|
||||
|
||||
for (let i = 0; i < indexAttributes.length; ++i) {
|
||||
const index = indexAttributes[i]
|
||||
|
||||
for (let j = 0; j < index.length; ++j) {
|
||||
mergedIndex.push(index[j] + indexOffset)
|
||||
}
|
||||
|
||||
indexOffset += positionAttributes.length
|
||||
}
|
||||
return mergedIndex
|
||||
}
|
||||
|
||||
static mergeGeometryData(geometries: GeometryData[]): GeometryData {
|
||||
const sampleAttributes = geometries[0].attributes
|
||||
const mergedGeometry = {
|
||||
attributes: {},
|
||||
bakeTransform: null,
|
||||
transform: null
|
||||
} as GeometryData
|
||||
|
||||
for (let i = 0; i < geometries.length; i++) {
|
||||
if (geometries[i].bakeTransform)
|
||||
Geometry.transformGeometryData(geometries[i], geometries[i].bakeTransform)
|
||||
}
|
||||
|
||||
if (sampleAttributes[GeometryAttributes.INDEX]) {
|
||||
const indexAttributes = geometries.map(
|
||||
(item) => item.attributes[GeometryAttributes.INDEX]
|
||||
)
|
||||
const positionAttributes = geometries.map(
|
||||
(item) => item.attributes[GeometryAttributes.POSITION]
|
||||
)
|
||||
mergedGeometry.attributes[GeometryAttributes.INDEX] =
|
||||
Geometry.mergeIndexAttribute(indexAttributes, positionAttributes)
|
||||
}
|
||||
|
||||
for (const k in sampleAttributes) {
|
||||
if (k !== GeometryAttributes.INDEX) {
|
||||
const attributes = geometries.map((item) => {
|
||||
return item.attributes[k]
|
||||
})
|
||||
mergedGeometry.attributes[k] = Geometry.mergeGeometryAttribute(
|
||||
attributes,
|
||||
new Float32Array(attributes.reduce((prev, cur) => prev + cur.length, 0))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
geometries.forEach((geometry) => {
|
||||
for (const k in geometry.attributes) {
|
||||
delete geometry.attributes[k]
|
||||
}
|
||||
})
|
||||
|
||||
return mergedGeometry
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param wrappers TEMPORARY!!!
|
||||
* @returns
|
||||
*/
|
||||
public static applyWorldTransform(wrappers: Array<ObjectWrapper>) {
|
||||
const worldCenter = World.worldBox.getCenter(new Vector3())
|
||||
worldCenter.negate()
|
||||
const transform = new Matrix4().setPosition(worldCenter)
|
||||
World.worldBox.makeEmpty()
|
||||
for (let k = 0; k < wrappers.length; k++) {
|
||||
const wrapper = wrappers[k]
|
||||
if (Array.isArray(wrapper.bufferGeometry)) {
|
||||
Geometry.applyWorldTransform(wrapper.bufferGeometry)
|
||||
return
|
||||
}
|
||||
try {
|
||||
wrapper.bufferGeometry.applyMatrix4(transform)
|
||||
wrapper.bufferGeometry.computeBoundingBox()
|
||||
World.expandWorld(wrapper.bufferGeometry.boundingBox)
|
||||
Geometry.updateRTEGeometry(wrapper.bufferGeometry)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static transformGeometryData(geometryData: GeometryData, m: Matrix4) {
|
||||
if (!geometryData.attributes.POSITION) return
|
||||
|
||||
const e = m.elements
|
||||
|
||||
for (let k = 0; k < geometryData.attributes.POSITION.length; k += 3) {
|
||||
const x = geometryData.attributes.POSITION[k],
|
||||
y = geometryData.attributes.POSITION[k + 1],
|
||||
z = geometryData.attributes.POSITION[k + 2]
|
||||
const w = 1 / (e[3] * x + e[7] * y + e[11] * z + e[15])
|
||||
|
||||
geometryData.attributes.POSITION[k] = (e[0] * x + e[4] * y + e[8] * z + e[12]) * w
|
||||
geometryData.attributes.POSITION[k + 1] =
|
||||
(e[1] * x + e[5] * y + e[9] * z + e[13]) * w
|
||||
geometryData.attributes.POSITION[k + 2] =
|
||||
(e[2] * x + e[6] * y + e[10] * z + e[14]) * w
|
||||
}
|
||||
}
|
||||
|
||||
public static unpackColors(int32Colors: number[]): number[] {
|
||||
const colors = new Array<number>(int32Colors.length * 3)
|
||||
for (let i = 0; i < int32Colors.length; i++) {
|
||||
const color = int32Colors[i]
|
||||
const r = (color >> 16) & 0xff
|
||||
const g = (color >> 8) & 0xff
|
||||
const b = color & 0xff
|
||||
colors[i * 3] = r / 255
|
||||
colors[i * 3 + 1] = g / 255
|
||||
colors[i * 3 + 2] = b / 255
|
||||
}
|
||||
return colors
|
||||
}
|
||||
|
||||
public static DoubleToHighLowVector(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
|
||||
}
|
||||
}
|
||||
|
||||
public static DoubleToHighLowBuffer(
|
||||
input: ArrayLike<number>,
|
||||
position_low: number[] | Float32Array,
|
||||
position_high: number[] | Float32Array
|
||||
) {
|
||||
for (let k = 0; k < input.length; k++) {
|
||||
const doubleValue = input[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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static InterleaveBuffers(
|
||||
input0: number[] | Float32Array,
|
||||
input1: number[] | Float32Array
|
||||
) {
|
||||
const out = new Array<number>(input0.length + input1.length)
|
||||
for (let k = 0, l = 0; k < out.length; k += 6, l += 3) {
|
||||
out[k] = input0[k]
|
||||
out[k + 1] = input0[k + 1]
|
||||
out[k + 2] = input0[k + 2]
|
||||
|
||||
out[k + 3] = input1[k]
|
||||
out[k + 4] = input1[k + 1]
|
||||
out[k + 5] = input1[k + 2]
|
||||
}
|
||||
return out
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable camelcase */
|
||||
import { speckle_basic_vert } from './shaders/speckle-basic-vert'
|
||||
import { speckle_basic_frag } from './shaders/speckle-basic-frag'
|
||||
import { UniformsUtils, ShaderLib, Vector3, MeshBasicMaterial } from 'three'
|
||||
import { Matrix4 } from 'three'
|
||||
import { Geometry } from '../converter/Geometry'
|
||||
|
||||
class SpeckleBasicMaterial extends MeshBasicMaterial {
|
||||
private static readonly matBuff: Matrix4 = new Matrix4()
|
||||
private static readonly vecBuff0: Vector3 = new Vector3()
|
||||
private static readonly vecBuff1: Vector3 = new Vector3()
|
||||
private static readonly vecBuff2: Vector3 = new Vector3()
|
||||
|
||||
constructor(parameters, defines = []) {
|
||||
super(parameters)
|
||||
|
||||
this.userData.uViewer_high = {
|
||||
value: new Vector3()
|
||||
}
|
||||
this.userData.uViewer_low = {
|
||||
value: new Vector3()
|
||||
}
|
||||
;(this as any).vertProgram = speckle_basic_vert
|
||||
;(this as any).fragProgram = speckle_basic_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
|
||||
}
|
||||
|
||||
if (defines) {
|
||||
this.defines = {}
|
||||
}
|
||||
for (let 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()
|
||||
}
|
||||
|
||||
if (Geometry.USE_RTE) {
|
||||
this.defines['USE_RTE'] = ' '
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
onBeforeRender(_this, scene, camera, geometry, object, group) {
|
||||
if (Geometry.USE_RTE) {
|
||||
SpeckleBasicMaterial.matBuff.copy(camera.matrixWorldInverse)
|
||||
SpeckleBasicMaterial.matBuff.elements[12] = 0
|
||||
SpeckleBasicMaterial.matBuff.elements[13] = 0
|
||||
SpeckleBasicMaterial.matBuff.elements[14] = 0
|
||||
SpeckleBasicMaterial.matBuff.multiply(object.matrixWorld)
|
||||
object.modelViewMatrix.copy(SpeckleBasicMaterial.matBuff)
|
||||
|
||||
SpeckleBasicMaterial.vecBuff0.set(
|
||||
camera.matrixWorld.elements[12],
|
||||
camera.matrixWorld.elements[13],
|
||||
camera.matrixWorld.elements[14]
|
||||
)
|
||||
|
||||
Geometry.DoubleToHighLowVector(
|
||||
SpeckleBasicMaterial.vecBuff0,
|
||||
SpeckleBasicMaterial.vecBuff1,
|
||||
SpeckleBasicMaterial.vecBuff2
|
||||
)
|
||||
|
||||
this.userData.uViewer_low.value.copy(SpeckleBasicMaterial.vecBuff1)
|
||||
this.userData.uViewer_high.value.copy(SpeckleBasicMaterial.vecBuff2)
|
||||
|
||||
this.needsUpdate = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default SpeckleBasicMaterial
|
||||
@@ -0,0 +1,100 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable camelcase */
|
||||
import { speckle_lambert_vert } from './shaders/speckle-lambert-vert'
|
||||
import { speckle_lambert_frag } from './shaders/speckle-lambert-frag'
|
||||
import { UniformsUtils, ShaderLib, Vector3, MeshLambertMaterial } from 'three'
|
||||
import { Matrix4 } from 'three'
|
||||
import { Geometry } from '../converter/Geometry'
|
||||
|
||||
class SpeckleLambertMaterial extends MeshLambertMaterial {
|
||||
private static readonly matBuff: Matrix4 = new Matrix4()
|
||||
private static readonly vecBuff0: Vector3 = new Vector3()
|
||||
private static readonly vecBuff1: Vector3 = new Vector3()
|
||||
private static readonly vecBuff2: Vector3 = new Vector3()
|
||||
|
||||
constructor(parameters, defines = []) {
|
||||
super(parameters)
|
||||
|
||||
this.userData.uViewer_high = {
|
||||
value: new Vector3()
|
||||
}
|
||||
this.userData.uViewer_low = {
|
||||
value: new Vector3()
|
||||
}
|
||||
;(this as any).vertProgram = speckle_lambert_vert
|
||||
;(this as any).fragProgram = speckle_lambert_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
|
||||
}
|
||||
|
||||
if (defines) {
|
||||
this.defines = {}
|
||||
}
|
||||
for (let 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()
|
||||
}
|
||||
|
||||
if (Geometry.USE_RTE) {
|
||||
this.defines['USE_RTE'] = ' '
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
onBeforeRender(_this, scene, camera, geometry, object, group) {
|
||||
if (Geometry.USE_RTE) {
|
||||
SpeckleLambertMaterial.matBuff.copy(camera.matrixWorldInverse)
|
||||
SpeckleLambertMaterial.matBuff.elements[12] = 0
|
||||
SpeckleLambertMaterial.matBuff.elements[13] = 0
|
||||
SpeckleLambertMaterial.matBuff.elements[14] = 0
|
||||
SpeckleLambertMaterial.matBuff.multiply(object.matrixWorld)
|
||||
object.modelViewMatrix.copy(SpeckleLambertMaterial.matBuff)
|
||||
|
||||
SpeckleLambertMaterial.vecBuff0.set(
|
||||
camera.matrixWorld.elements[12],
|
||||
camera.matrixWorld.elements[13],
|
||||
camera.matrixWorld.elements[14]
|
||||
)
|
||||
|
||||
Geometry.DoubleToHighLowVector(
|
||||
SpeckleLambertMaterial.vecBuff0,
|
||||
SpeckleLambertMaterial.vecBuff1,
|
||||
SpeckleLambertMaterial.vecBuff2
|
||||
)
|
||||
|
||||
this.userData.uViewer_low.value.copy(SpeckleLambertMaterial.vecBuff1)
|
||||
this.userData.uViewer_high.value.copy(SpeckleLambertMaterial.vecBuff2)
|
||||
|
||||
this.needsUpdate = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default SpeckleLambertMaterial
|
||||
@@ -0,0 +1,97 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable camelcase */
|
||||
import { speckle_line_basic_vert } from './shaders/speckle-line-basic-vert'
|
||||
import { speckle_line_basic_frag } from './shaders/speckle-line-basic-frag'
|
||||
import { UniformsUtils, ShaderLib, Vector3, LineBasicMaterial } from 'three'
|
||||
import { Matrix4 } from 'three'
|
||||
import { Geometry } from '../converter/Geometry'
|
||||
|
||||
class SpeckleLineBasicMaterial extends LineBasicMaterial {
|
||||
private static readonly matBuff: Matrix4 = new Matrix4()
|
||||
private static readonly vecBuff0: Vector3 = new Vector3()
|
||||
private static readonly vecBuff1: Vector3 = new Vector3()
|
||||
private static readonly vecBuff2: Vector3 = new Vector3()
|
||||
|
||||
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_basic_vert
|
||||
;(this as any).fragProgram = speckle_line_basic_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 (let k = 0; k < defines.length; k++) {
|
||||
this.defines[defines[k]] = ''
|
||||
}
|
||||
|
||||
if (Geometry.USE_RTE) {
|
||||
this.defines = {}
|
||||
this.defines['USE_RTE'] = ' '
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if (Geometry.USE_RTE) {
|
||||
SpeckleLineBasicMaterial.matBuff.copy(camera.matrixWorldInverse)
|
||||
SpeckleLineBasicMaterial.matBuff.elements[12] = 0
|
||||
SpeckleLineBasicMaterial.matBuff.elements[13] = 0
|
||||
SpeckleLineBasicMaterial.matBuff.elements[14] = 0
|
||||
SpeckleLineBasicMaterial.matBuff.multiply(object.matrixWorld)
|
||||
object.modelViewMatrix.copy(SpeckleLineBasicMaterial.matBuff)
|
||||
|
||||
SpeckleLineBasicMaterial.vecBuff0.set(
|
||||
camera.matrixWorld.elements[12],
|
||||
camera.matrixWorld.elements[13],
|
||||
camera.matrixWorld.elements[14]
|
||||
)
|
||||
|
||||
Geometry.DoubleToHighLowVector(
|
||||
SpeckleLineBasicMaterial.vecBuff0,
|
||||
SpeckleLineBasicMaterial.vecBuff1,
|
||||
SpeckleLineBasicMaterial.vecBuff2
|
||||
)
|
||||
this.userData.uViewer_low.value.copy(SpeckleLineBasicMaterial.vecBuff1)
|
||||
this.userData.uViewer_high.value.copy(SpeckleLineBasicMaterial.vecBuff2)
|
||||
|
||||
this.needsUpdate = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default SpeckleLineBasicMaterial
|
||||
@@ -0,0 +1,111 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable camelcase */
|
||||
import { speckle_line_vert } from './shaders/speckle-line-vert'
|
||||
import { speckle_line_frag } from './shaders/speckle-line-frag'
|
||||
import { UniformsUtils, ShaderLib, Vector3 } 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()
|
||||
private static readonly vecBuff0: Vector3 = new Vector3()
|
||||
private static readonly vecBuff1: Vector3 = new Vector3()
|
||||
private static readonly vecBuff2: Vector3 = new Vector3()
|
||||
public set pixelThreshold(value: number) {
|
||||
this.userData.pixelThreshold.value = value
|
||||
this.needsUpdate = true
|
||||
}
|
||||
|
||||
constructor(parameters, defines = []) {
|
||||
super(parameters)
|
||||
|
||||
this.userData.uViewer_high = {
|
||||
value: new Vector3()
|
||||
}
|
||||
this.userData.uViewer_low = {
|
||||
value: new Vector3()
|
||||
}
|
||||
this.userData.pixelThreshold = {
|
||||
value: 0
|
||||
}
|
||||
;(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
|
||||
},
|
||||
pixelThreshold: {
|
||||
value: this.userData.pixelThreshold
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
this.onBeforeCompile = function (shader) {
|
||||
shader.uniforms.uViewer_high = this.userData.uViewer_high
|
||||
shader.uniforms.uViewer_low = this.userData.uViewer_low
|
||||
shader.uniforms.pixelThreshold = this.userData.pixelThreshold
|
||||
shader.vertexShader = this.vertProgram
|
||||
shader.fragmentShader = this.fragProgram
|
||||
}
|
||||
|
||||
for (let k = 0; k < defines.length; k++) {
|
||||
this.defines[defines[k]] = ''
|
||||
}
|
||||
|
||||
if (Geometry.USE_RTE) {
|
||||
this.defines['USE_RTE'] = ' '
|
||||
}
|
||||
}
|
||||
|
||||
copy(source) {
|
||||
super.copy(source)
|
||||
this.userData = {}
|
||||
this.userData.uViewer_high = {
|
||||
value: new Vector3()
|
||||
}
|
||||
this.userData.uViewer_low = {
|
||||
value: new Vector3()
|
||||
}
|
||||
this.userData.pixelThreshold = {
|
||||
value: source.userData.pixelThreshold.value
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
onBeforeRender(_this, scene, camera, geometry, object, group) {
|
||||
if (Geometry.USE_RTE) {
|
||||
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)
|
||||
|
||||
SpeckleLineMaterial.vecBuff0.set(
|
||||
camera.matrixWorld.elements[12],
|
||||
camera.matrixWorld.elements[13],
|
||||
camera.matrixWorld.elements[14]
|
||||
)
|
||||
|
||||
Geometry.DoubleToHighLowVector(
|
||||
SpeckleLineMaterial.vecBuff0,
|
||||
SpeckleLineMaterial.vecBuff1,
|
||||
SpeckleLineMaterial.vecBuff2
|
||||
)
|
||||
this.userData.uViewer_low.value.copy(SpeckleLineMaterial.vecBuff1)
|
||||
this.userData.uViewer_high.value.copy(SpeckleLineMaterial.vecBuff2)
|
||||
|
||||
this.needsUpdate = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default SpeckleLineMaterial
|
||||
@@ -0,0 +1,100 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable camelcase */
|
||||
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()
|
||||
private static readonly vecBuff0: Vector3 = new Vector3()
|
||||
private static readonly vecBuff1: Vector3 = new Vector3()
|
||||
private static readonly vecBuff2: Vector3 = new Vector3()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if (defines) {
|
||||
this.defines = {}
|
||||
}
|
||||
for (let 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()
|
||||
}
|
||||
|
||||
if (Geometry.USE_RTE) {
|
||||
this.defines['USE_RTE'] = ' '
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
onBeforeRender(_this, scene, camera, geometry, object, group) {
|
||||
if (Geometry.USE_RTE) {
|
||||
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)
|
||||
|
||||
SpeckleStandardMaterial.vecBuff0.set(
|
||||
camera.matrixWorld.elements[12],
|
||||
camera.matrixWorld.elements[13],
|
||||
camera.matrixWorld.elements[14]
|
||||
)
|
||||
|
||||
Geometry.DoubleToHighLowVector(
|
||||
SpeckleStandardMaterial.vecBuff0,
|
||||
SpeckleStandardMaterial.vecBuff1,
|
||||
SpeckleStandardMaterial.vecBuff2
|
||||
)
|
||||
|
||||
this.userData.uViewer_low.value.copy(SpeckleStandardMaterial.vecBuff1)
|
||||
this.userData.uViewer_high.value.copy(SpeckleStandardMaterial.vecBuff2)
|
||||
|
||||
this.needsUpdate = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default SpeckleStandardMaterial
|
||||
@@ -0,0 +1,53 @@
|
||||
export const speckle_basic_frag = /* glsl */ `
|
||||
uniform vec3 diffuse;
|
||||
uniform float opacity;
|
||||
#ifndef FLAT_SHADED
|
||||
varying vec3 vNormal;
|
||||
#endif
|
||||
#include <common>
|
||||
#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 <envmap_common_pars_fragment>
|
||||
#include <envmap_pars_fragment>
|
||||
#include <cube_uv_reflection_fragment>
|
||||
#include <fog_pars_fragment>
|
||||
#include <specularmap_pars_fragment>
|
||||
#include <logdepthbuf_pars_fragment>
|
||||
#include <clipping_planes_pars_fragment>
|
||||
void main() {
|
||||
#include <clipping_planes_fragment>
|
||||
vec4 diffuseColor = vec4( diffuse, opacity );
|
||||
#include <logdepthbuf_fragment>
|
||||
#include <map_fragment>
|
||||
#include <color_fragment>
|
||||
#include <alphamap_fragment>
|
||||
#include <alphatest_fragment>
|
||||
#include <specularmap_fragment>
|
||||
ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
|
||||
// accumulation (baked indirect lighting only)
|
||||
#ifdef USE_LIGHTMAP
|
||||
vec4 lightMapTexel = texture2D( lightMap, vUv2 );
|
||||
reflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity * RECIPROCAL_PI;
|
||||
#else
|
||||
reflectedLight.indirectDiffuse += vec3( 1.0 );
|
||||
#endif
|
||||
// modulation
|
||||
#include <aomap_fragment>
|
||||
reflectedLight.indirectDiffuse *= diffuseColor.rgb;
|
||||
vec3 outgoingLight = reflectedLight.indirectDiffuse;
|
||||
#include <envmap_fragment>
|
||||
#include <output_fragment>
|
||||
#include <tonemapping_fragment>
|
||||
#include <encodings_fragment>
|
||||
#include <fog_fragment>
|
||||
#include <premultiplied_alpha_fragment>
|
||||
#include <dithering_fragment>
|
||||
}
|
||||
`
|
||||
@@ -0,0 +1,56 @@
|
||||
export const speckle_basic_vert = /* glsl */ `
|
||||
#include <common>
|
||||
#ifdef USE_RTE
|
||||
attribute vec3 position_high;
|
||||
attribute vec3 position_low;
|
||||
uniform vec3 uViewer_high;
|
||||
uniform vec3 uViewer_low;
|
||||
#endif
|
||||
#include <uv_pars_vertex>
|
||||
#include <uv2_pars_vertex>
|
||||
#include <envmap_pars_vertex>
|
||||
#include <color_pars_vertex>
|
||||
#include <fog_pars_vertex>
|
||||
#include <morphtarget_pars_vertex>
|
||||
#include <skinning_pars_vertex>
|
||||
#include <logdepthbuf_pars_vertex>
|
||||
#include <clipping_planes_pars_vertex>
|
||||
void main() {
|
||||
#include <uv_vertex>
|
||||
#include <uv2_vertex>
|
||||
#include <color_vertex>
|
||||
#include <morphcolor_vertex>
|
||||
#if defined ( USE_ENVMAP ) || defined ( USE_SKINNING )
|
||||
#include <beginnormal_vertex>
|
||||
#include <morphnormal_vertex>
|
||||
#include <skinbase_vertex>
|
||||
#include <skinnormal_vertex>
|
||||
#include <defaultnormal_vertex>
|
||||
#endif
|
||||
#include <begin_vertex>
|
||||
#include <morphtarget_vertex>
|
||||
#include <skinning_vertex>
|
||||
// #include <project_vertex> COMMENTED CHUNK
|
||||
#ifdef USE_RTE
|
||||
vec3 highDifference = vec3(position_high.xyz - uViewer_high);
|
||||
vec3 lowDifference = vec3(position_low.xyz - uViewer_low);
|
||||
vec4 mvPosition = vec4(highDifference.xyz + lowDifference.xyz , 1.);
|
||||
#else
|
||||
vec4 mvPosition = vec4( transformed, 1.0 );
|
||||
#endif
|
||||
|
||||
#ifdef USE_INSTANCING
|
||||
|
||||
mvPosition = instanceMatrix * mvPosition;
|
||||
|
||||
#endif
|
||||
mvPosition = modelViewMatrix * mvPosition;
|
||||
|
||||
gl_Position = projectionMatrix * mvPosition;
|
||||
#include <logdepthbuf_vertex>
|
||||
#include <clipping_planes_vertex>
|
||||
#include <worldpos_vertex>
|
||||
#include <envmap_vertex>
|
||||
#include <fog_vertex>
|
||||
}
|
||||
`
|
||||
@@ -0,0 +1,71 @@
|
||||
export const speckle_lambert_frag = /* glsl */ `
|
||||
uniform vec3 diffuse;
|
||||
uniform vec3 emissive;
|
||||
uniform float opacity;
|
||||
varying vec3 vLightFront;
|
||||
varying vec3 vIndirectFront;
|
||||
#ifdef DOUBLE_SIDED
|
||||
varying vec3 vLightBack;
|
||||
varying vec3 vIndirectBack;
|
||||
#endif
|
||||
#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 <envmap_common_pars_fragment>
|
||||
#include <envmap_pars_fragment>
|
||||
#include <cube_uv_reflection_fragment>
|
||||
#include <bsdfs>
|
||||
#include <lights_pars_begin>
|
||||
#include <fog_pars_fragment>
|
||||
#include <shadowmap_pars_fragment>
|
||||
#include <shadowmask_pars_fragment>
|
||||
#include <specularmap_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 <specularmap_fragment>
|
||||
#include <emissivemap_fragment>
|
||||
// accumulation
|
||||
#ifdef DOUBLE_SIDED
|
||||
reflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack;
|
||||
#else
|
||||
reflectedLight.indirectDiffuse += vIndirectFront;
|
||||
#endif
|
||||
#include <lightmap_fragment>
|
||||
reflectedLight.indirectDiffuse *= BRDF_Lambert( diffuseColor.rgb );
|
||||
#ifdef DOUBLE_SIDED
|
||||
reflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;
|
||||
#else
|
||||
reflectedLight.directDiffuse = vLightFront;
|
||||
#endif
|
||||
reflectedLight.directDiffuse *= BRDF_Lambert( diffuseColor.rgb ) * getShadowMask();
|
||||
// modulation
|
||||
#include <aomap_fragment>
|
||||
vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;
|
||||
#include <envmap_fragment>
|
||||
#include <output_fragment>
|
||||
#include <tonemapping_fragment>
|
||||
#include <encodings_fragment>
|
||||
#include <fog_fragment>
|
||||
#include <premultiplied_alpha_fragment>
|
||||
#include <dithering_fragment>
|
||||
}
|
||||
`
|
||||
@@ -0,0 +1,67 @@
|
||||
export const speckle_lambert_vert = /* glsl */ `
|
||||
#define LAMBERT
|
||||
#ifdef USE_RTE
|
||||
attribute vec3 position_high;
|
||||
attribute vec3 position_low;
|
||||
uniform vec3 uViewer_high;
|
||||
uniform vec3 uViewer_low;
|
||||
#endif
|
||||
|
||||
varying vec3 vLightFront;
|
||||
varying vec3 vIndirectFront;
|
||||
#ifdef DOUBLE_SIDED
|
||||
varying vec3 vLightBack;
|
||||
varying vec3 vIndirectBack;
|
||||
#endif
|
||||
#include <common>
|
||||
#include <uv_pars_vertex>
|
||||
#include <uv2_pars_vertex>
|
||||
#include <envmap_pars_vertex>
|
||||
#include <bsdfs>
|
||||
#include <lights_pars_begin>
|
||||
#include <color_pars_vertex>
|
||||
#include <fog_pars_vertex>
|
||||
#include <morphtarget_pars_vertex>
|
||||
#include <skinning_pars_vertex>
|
||||
#include <shadowmap_pars_vertex>
|
||||
#include <logdepthbuf_pars_vertex>
|
||||
#include <clipping_planes_pars_vertex>
|
||||
void main() {
|
||||
#include <uv_vertex>
|
||||
#include <uv2_vertex>
|
||||
#include <color_vertex>
|
||||
#include <morphcolor_vertex>
|
||||
#include <beginnormal_vertex>
|
||||
#include <morphnormal_vertex>
|
||||
#include <skinbase_vertex>
|
||||
#include <skinnormal_vertex>
|
||||
#include <defaultnormal_vertex>
|
||||
#include <begin_vertex>
|
||||
#include <morphtarget_vertex>
|
||||
#include <skinning_vertex>
|
||||
//#include <project_vertex> COMMENTED CHUNK
|
||||
#ifdef USE_RTE
|
||||
vec3 highDifference = vec3(position_high.xyz - uViewer_high);
|
||||
vec3 lowDifference = vec3(position_low.xyz - uViewer_low);
|
||||
vec4 mvPosition = vec4(highDifference.xyz + lowDifference.xyz , 1.);
|
||||
#else
|
||||
vec4 mvPosition = vec4( transformed, 1.0 );
|
||||
#endif
|
||||
|
||||
#ifdef USE_INSTANCING
|
||||
|
||||
mvPosition = instanceMatrix * mvPosition;
|
||||
|
||||
#endif
|
||||
mvPosition = modelViewMatrix * mvPosition;
|
||||
|
||||
gl_Position = projectionMatrix * mvPosition;
|
||||
#include <logdepthbuf_vertex>
|
||||
#include <clipping_planes_vertex>
|
||||
#include <worldpos_vertex>
|
||||
#include <envmap_vertex>
|
||||
#include <lights_lambert_vertex>
|
||||
#include <shadowmap_vertex>
|
||||
#include <fog_vertex>
|
||||
}
|
||||
`
|
||||
@@ -0,0 +1,53 @@
|
||||
export const speckle_line_basic_frag = /* glsl */ `
|
||||
uniform vec3 diffuse;
|
||||
uniform float opacity;
|
||||
#ifndef FLAT_SHADED
|
||||
varying vec3 vNormal;
|
||||
#endif
|
||||
#include <common>
|
||||
#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 <envmap_common_pars_fragment>
|
||||
#include <envmap_pars_fragment>
|
||||
#include <cube_uv_reflection_fragment>
|
||||
#include <fog_pars_fragment>
|
||||
#include <specularmap_pars_fragment>
|
||||
#include <logdepthbuf_pars_fragment>
|
||||
#include <clipping_planes_pars_fragment>
|
||||
void main() {
|
||||
#include <clipping_planes_fragment>
|
||||
vec4 diffuseColor = vec4( diffuse, opacity );
|
||||
#include <logdepthbuf_fragment>
|
||||
#include <map_fragment>
|
||||
#include <color_fragment>
|
||||
#include <alphamap_fragment>
|
||||
#include <alphatest_fragment>
|
||||
#include <specularmap_fragment>
|
||||
ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
|
||||
// accumulation (baked indirect lighting only)
|
||||
#ifdef USE_LIGHTMAP
|
||||
vec4 lightMapTexel = texture2D( lightMap, vUv2 );
|
||||
reflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity * RECIPROCAL_PI;
|
||||
#else
|
||||
reflectedLight.indirectDiffuse += vec3( 1.0 );
|
||||
#endif
|
||||
// modulation
|
||||
#include <aomap_fragment>
|
||||
reflectedLight.indirectDiffuse *= diffuseColor.rgb;
|
||||
vec3 outgoingLight = reflectedLight.indirectDiffuse;
|
||||
#include <envmap_fragment>
|
||||
#include <output_fragment>
|
||||
#include <tonemapping_fragment>
|
||||
#include <encodings_fragment>
|
||||
#include <fog_fragment>
|
||||
#include <premultiplied_alpha_fragment>
|
||||
#include <dithering_fragment>
|
||||
}
|
||||
`
|
||||
@@ -0,0 +1,52 @@
|
||||
export const speckle_line_basic_vert = /* glsl */ `
|
||||
#include <common>
|
||||
#include <uv_pars_vertex>
|
||||
#include <uv2_pars_vertex>
|
||||
#include <envmap_pars_vertex>
|
||||
#include <color_pars_vertex>
|
||||
#include <fog_pars_vertex>
|
||||
#include <morphtarget_pars_vertex>
|
||||
#include <skinning_pars_vertex>
|
||||
#include <logdepthbuf_pars_vertex>
|
||||
#include <clipping_planes_pars_vertex>
|
||||
#ifdef USE_RTE
|
||||
attribute vec3 position_high;
|
||||
attribute vec3 position_low;
|
||||
uniform vec3 uViewer_high;
|
||||
uniform vec3 uViewer_low;
|
||||
#endif
|
||||
void main() {
|
||||
#include <uv_vertex>
|
||||
#include <uv2_vertex>
|
||||
#include <color_vertex>
|
||||
#include <morphcolor_vertex>
|
||||
#if defined ( USE_ENVMAP ) || defined ( USE_SKINNING )
|
||||
#include <beginnormal_vertex>
|
||||
#include <morphnormal_vertex>
|
||||
#include <skinbase_vertex>
|
||||
#include <skinnormal_vertex>
|
||||
#include <defaultnormal_vertex>
|
||||
#endif
|
||||
#include <begin_vertex>
|
||||
#include <morphtarget_vertex>
|
||||
#include <skinning_vertex>
|
||||
// #include <project_vertex> COMMENTED CHUNK
|
||||
#ifdef USE_RTE
|
||||
vec3 highDifference = vec3(position_high.xyz - uViewer_high);
|
||||
vec3 lowDifference = vec3(position_low.xyz - uViewer_low);
|
||||
vec4 mvPosition = vec4(highDifference.xyz + lowDifference.xyz , 1.);
|
||||
#else
|
||||
vec4 mvPosition = vec4( transformed, 1.0 );
|
||||
#endif
|
||||
#ifdef USE_INSTANCING
|
||||
mvPosition = instanceMatrix * mvPosition;
|
||||
#endif
|
||||
mvPosition = modelViewMatrix * mvPosition;
|
||||
gl_Position = projectionMatrix * mvPosition;
|
||||
#include <logdepthbuf_vertex>
|
||||
#include <clipping_planes_vertex>
|
||||
#include <worldpos_vertex>
|
||||
#include <envmap_vertex>
|
||||
#include <fog_vertex>
|
||||
}
|
||||
`
|
||||
@@ -0,0 +1,162 @@
|
||||
export const speckle_line_frag = /* glsl */ `
|
||||
uniform vec3 diffuse;
|
||||
uniform float opacity;
|
||||
uniform float linewidth;
|
||||
// varying vec3 debugColor;
|
||||
|
||||
#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;
|
||||
varying float correctedLineWidth;
|
||||
|
||||
#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 / correctedLineWidth;
|
||||
|
||||
#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,283 @@
|
||||
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 float pixelThreshold;
|
||||
#define SEARCH_STEPS 10
|
||||
|
||||
attribute vec3 instanceStart;
|
||||
attribute vec3 instanceEnd;
|
||||
|
||||
attribute vec3 instanceColorStart;
|
||||
attribute vec3 instanceColorEnd;
|
||||
// varying vec3 debugColor;
|
||||
|
||||
#ifdef WORLD_UNITS
|
||||
|
||||
varying vec4 worldPos;
|
||||
varying vec3 worldStart;
|
||||
varying vec3 worldEnd;
|
||||
varying float correctedLineWidth;
|
||||
|
||||
#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
|
||||
|
||||
#ifdef USE_RTE
|
||||
// attribute vec3 position_high;
|
||||
// attribute vec3 position_low;
|
||||
attribute vec3 instanceStartLow;
|
||||
attribute vec3 instanceStartHigh;
|
||||
attribute vec3 instanceEndLow;
|
||||
attribute vec3 instanceEndHigh;
|
||||
uniform vec3 uViewer_high;
|
||||
uniform vec3 uViewer_low;
|
||||
#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 );
|
||||
|
||||
}
|
||||
|
||||
float screenSpaceDistance(vec4 p0, vec4 p1) {
|
||||
p0 = projectionMatrix * p0;
|
||||
p0 /= p0.w;
|
||||
p1 = projectionMatrix * p1;
|
||||
p1 /= p1.w;
|
||||
return length(p1.xy - p0.xy);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 computedPosition = position;
|
||||
#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
|
||||
#ifdef USE_RTE
|
||||
vec3 startHighDifference = vec3(instanceStartHigh.xyz - uViewer_high);
|
||||
vec3 startLowDifference = vec3(instanceStartLow.xyz - uViewer_low);
|
||||
vec3 endHighDifference = vec3(instanceEndHigh.xyz - uViewer_high);
|
||||
vec3 endLowDifference = vec3(instanceEndLow.xyz - uViewer_low);
|
||||
vec4 start = modelViewMatrix * vec4( startLowDifference + startHighDifference, 1.0 );
|
||||
vec4 end = modelViewMatrix * vec4( endLowDifference + endHighDifference, 1.0 );
|
||||
#else
|
||||
vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 );
|
||||
vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 );
|
||||
#endif
|
||||
|
||||
#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;
|
||||
|
||||
}
|
||||
|
||||
// debugColor = vec3(0., 0., 1.);
|
||||
correctedLineWidth = linewidth;
|
||||
vec3 cOffset = offset;
|
||||
|
||||
// adjust for linewidth
|
||||
offset *= linewidth * 0.5;
|
||||
|
||||
// set the world position
|
||||
worldPos = ( computedPosition.y < 0.5 ) ? start : end;
|
||||
|
||||
/*
|
||||
Not great, not terrible
|
||||
*/
|
||||
float d;
|
||||
float offsetStep = linewidth;
|
||||
vec3 move = offset;
|
||||
float pixelSize = length(vec2(pixelThreshold/resolution.x + pixelThreshold/resolution.y));
|
||||
for(int i = 0; i < SEARCH_STEPS; i++){
|
||||
move = cOffset * offsetStep;
|
||||
d = screenSpaceDistance(worldPos, worldPos + vec4(move, 0.));
|
||||
if(d > pixelSize) {
|
||||
correctedLineWidth = offsetStep;
|
||||
break;
|
||||
}
|
||||
offsetStep += offsetStep;
|
||||
}
|
||||
|
||||
worldPos.xyz += move;
|
||||
|
||||
// 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,84 @@
|
||||
export const speckle_standard_vert = /* glsl */ `
|
||||
#define STANDARD
|
||||
#ifdef USE_RTE
|
||||
attribute vec3 position_high;
|
||||
attribute vec3 position_low;
|
||||
uniform vec3 uViewer_high;
|
||||
uniform vec3 uViewer_low;
|
||||
#endif
|
||||
|
||||
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>
|
||||
|
||||
|
||||
|
||||
void main() {
|
||||
|
||||
#include <uv_vertex>
|
||||
#include <uv2_vertex>
|
||||
#include <color_vertex>
|
||||
#include <morphcolor_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
|
||||
#ifdef USE_RTE
|
||||
vec3 highDifference = vec3(position_high.xyz - uViewer_high);
|
||||
vec3 lowDifference = vec3(position_low.xyz - uViewer_low);
|
||||
vec4 mvPosition = vec4(highDifference.xyz + lowDifference.xyz , 1.);
|
||||
#else
|
||||
vec4 mvPosition = vec4( transformed, 1.0 );
|
||||
#endif
|
||||
|
||||
#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
|
||||
}
|
||||
`
|
||||
@@ -0,0 +1,9 @@
|
||||
declare module '*.png' {
|
||||
const value: string
|
||||
export default value
|
||||
}
|
||||
|
||||
declare module '*.hdr' {
|
||||
const value: string
|
||||
export default value
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2019",
|
||||
"module": "ESNext",
|
||||
"lib": ["DOM"],
|
||||
"moduleResolution": "Node",
|
||||
"strict": false,
|
||||
"sourceMap": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": false,
|
||||
"noImplicitReturns": false,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "./dist",
|
||||
"allowJs": true,
|
||||
"checkJs": false,
|
||||
"declaration": true
|
||||
},
|
||||
"include": ["./src/**/*"],
|
||||
"exclude": ["dist"]
|
||||
}
|
||||
Reference in New Issue
Block a user