Przeglądaj źródła

Add basic support for LineSegments2/fat lines, support for importing fat lines in gltf, add track param for material clone, small Box3B fix for lines, add examples for lines.

master
Palash Bansal 1 rok temu
rodzic
commit
f5a0f0b295
No account linked to committer's email address
35 zmienionych plików z 664 dodań i 60 usunięć
  1. 36
    0
      examples/fat-lines/index.html
  2. 55
    0
      examples/fat-lines/script.ts
  3. 36
    0
      examples/gltf-mesh-lines/index.html
  4. 84
    0
      examples/gltf-mesh-lines/script.ts
  5. 2
    0
      examples/index.html
  6. 4
    4
      package-lock.json
  7. 3
    3
      package.json
  8. 7
    0
      src/assetmanager/IAssetImporter.ts
  9. 87
    7
      src/assetmanager/import/GLTFLoader2.ts
  10. 11
    0
      src/core/IMaterial.ts
  11. 1
    0
      src/core/geometry/BufferGeometry2.ts
  12. 20
    0
      src/core/geometry/LineGeometry2.ts
  13. 19
    0
      src/core/geometry/LineSegmentsGeometry2.ts
  14. 23
    0
      src/core/geometry/WireframeGeometry3.ts
  15. 5
    0
      src/core/index.ts
  16. 1
    1
      src/core/material/LegacyPhongMaterial.ts
  17. 45
    5
      src/core/material/LineMaterial2.ts
  18. 2
    2
      src/core/material/ObjectShaderMaterial.ts
  19. 1
    1
      src/core/material/PhysicalMaterial.ts
  20. 1
    1
      src/core/material/ShaderMaterial2.ts
  21. 1
    1
      src/core/material/UnlitLineMaterial.ts
  22. 1
    1
      src/core/material/UnlitMaterial.ts
  23. 15
    10
      src/core/material/iMaterialCommons.ts
  24. 48
    8
      src/core/object/IObjectUi.ts
  25. 1
    0
      src/core/object/Mesh2.ts
  26. 55
    0
      src/core/object/MeshLine.ts
  27. 55
    0
      src/core/object/MeshLineSegments.ts
  28. 8
    2
      src/three/Threejs.ts
  29. 0
    1
      src/three/controls/OrbitControls3.ts
  30. 24
    7
      src/three/math/Box3B.ts
  31. 4
    1
      src/three/widgets/BoxSelectionWidget.ts
  32. 2
    0
      src/three/widgets/CameraHelper2.ts
  33. 2
    0
      src/three/widgets/DirectionalLightHelper2.ts
  34. 2
    0
      src/three/widgets/PointLightHelper2.ts
  35. 3
    5
      src/three/widgets/SpotLightHelper2.ts

+ 36
- 0
examples/fat-lines/index.html Wyświetl plik

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Fat Lines/Mesh Lines</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>

<script type="importmap">
{
"imports": {
"threepipe": "./../../dist/index.mjs",
"@threepipe/plugin-tweakpane": "./../../plugins/tweakpane/dist/index.mjs"
}
}

</script>
<style id="example-style">
html, body, #canvas-container, #mcanvas {
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
}
</style>
<script type="module" src="../examples-utils/simple-code-preview.mjs"></script>
<script id="example-script" type="module" src="./script.js" data-scripts="./script.ts;./script.js"></script>
</head>
<body>
<div id="canvas-container">
<canvas id="mcanvas"></canvas>
</div>

</body>

+ 55
- 0
examples/fat-lines/script.ts Wyświetl plik

import {
_testFinish,
Color,
GLTFLoader2,
IObject3D,
LineMaterial2,
LoadingScreenPlugin,
PickingPlugin, PopmotionPlugin,
ThreeViewer,
} from 'threepipe'
import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane'

async function init() {

const viewer = new ThreeViewer({
canvas: document.getElementById('mcanvas') as HTMLCanvasElement,
msaa: true,
plugins: [LoadingScreenPlugin, PickingPlugin],
dropzone: true,
})

viewer.scene.autoNearFarEnabled = false

GLTFLoader2.UseMeshLines = true

viewer.scene.backgroundColor = new Color(0x333333)

await viewer.load<IObject3D>('https://asset-samples.threepipe.org/demos/temple-lines.glb.zip', {
autoScale: true,
autoCenter: true,
})

const popmotion = viewer.addPluginSync(PopmotionPlugin)

const material = viewer.materialManager.findMaterialsByName('Stone')[0] as LineMaterial2

popmotion.animate({
from: 0,
to: 1,
duration: 1000,
repeat: Infinity,
repeatType: 'mirror',
onUpdate: (v) => {
material.linewidth = Math.sin(v * Math.PI) * 3.5 + 1
material.color.setHSL(v, 0.5, 0.7)
material.setDirty()
},
})

const ui = viewer.addPluginSync(new TweakpaneUiPlugin(true))
ui.setupPluginUi(PickingPlugin)

}

init().finally(_testFinish)

+ 36
- 0
examples/gltf-mesh-lines/index.html Wyświetl plik

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>glTF Mesh(Fat) Lines Import</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>

<script type="importmap">
{
"imports": {
"threepipe": "./../../dist/index.mjs",
"@threepipe/plugin-tweakpane": "./../../plugins/tweakpane/dist/index.mjs"
}
}

</script>
<style id="example-style">
html, body, #canvas-container, #mcanvas {
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
}
</style>
<script type="module" src="../examples-utils/simple-code-preview.mjs"></script>
<script id="example-script" type="module" src="./script.js" data-scripts="./script.ts;./script.js"></script>
</head>
<body>
<div id="canvas-container">
<canvas id="mcanvas"></canvas>
</div>

</body>

+ 84
- 0
examples/gltf-mesh-lines/script.ts Wyświetl plik

import {
_testFinish,
BufferGeometry,
BufferGeometry2,
Color,
GLTFLoader2,
IMaterial,
IObject3D,
LineSegmentsGeometry,
LineSegmentsGeometry2,
LoadingScreenPlugin,
Object3D,
ThreeViewer,
} from 'threepipe'
import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane'

async function init() {

const viewer = new ThreeViewer({
canvas: document.getElementById('mcanvas') as HTMLCanvasElement,
msaa: true,
plugins: [LoadingScreenPlugin],
dropzone: true,
})

viewer.scene.autoNearFarEnabled = false

GLTFLoader2.UseMeshLines = true

viewer.scene.backgroundColor = new Color(0x333333)

// await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr')
const obj1 = await viewer.load<IObject3D>('https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/refs/heads/main/Models/MeshPrimitiveModes/glTF/MeshPrimitiveModes.gltf', {
autoScale: true,
autoCenter: true,
useMeshLines: true,
})

const obj2 = await viewer.load<IObject3D>('https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/refs/heads/main/Models/MeshPrimitiveModes/glTF/MeshPrimitiveModes.gltf', {
autoScale: true,
autoCenter: true,
useMeshLines: false,
})

const ui = viewer.addPluginSync(new TweakpaneUiPlugin(true))

const mats: IMaterial[] = []
const o1: IObject3D[] = []
const o2: IObject3D[] = []
const p1 = viewer.scene.addObject(new Object3D()).translateY(0.75)
const p2 = viewer.scene.addObject(new Object3D()).translateY(-0.75)
p1.scale.setScalar(0.6)
p2.scale.setScalar(0.6)
obj1?.traverse(o=>{
if (o.materials?.[0].isLineMaterial) {
mats.push(o.materials[0])
o1.push(o)
}
})
obj2?.traverse(o=>{
if (o.materials?.[0].isUnlitLineMaterial) {
mats.push(o.materials[0])
o2.push(o)
}
})
o1.forEach(o=>p1.add(o))
o2.forEach(o=>p2.add(o))
obj1?.dispose(true)
obj2?.dispose(true)

mats.map(m=>{
if (!m.appliedMeshes.size) return
m.linewidth = 10
ui.appendChild(m.uiConfig)
})

console.log(LineSegmentsGeometry)
console.log(LineSegmentsGeometry2)
console.log(BufferGeometry)
console.log(BufferGeometry2)

}

init().finally(_testFinish)

+ 2
- 0
examples/index.html Wyświetl plik

<li><a href="./splat-load/">SPLAT Load<br/>(Gaussian Splatting) </a></li> <li><a href="./splat-load/">SPLAT Load<br/>(Gaussian Splatting) </a></li>
<li><a href="./extra-importer-plugins/">Extra (3ds, 3mf, collada, amf, bvh, vox, gcode, mdd, pcd, tilt, wrl, ldraw, vtk, xyz) Load </a></li> <li><a href="./extra-importer-plugins/">Extra (3ds, 3mf, collada, amf, bvh, vox, gcode, mdd, pcd, tilt, wrl, ldraw, vtk, xyz) Load </a></li>
<li><a href="./gltf-meshopt-compression/">glTF MeshOpt Decode (Compression Extension) </a></li> <li><a href="./gltf-meshopt-compression/">glTF MeshOpt Decode (Compression Extension) </a></li>
<li><a href="./gltf-mesh-lines/">glTF Mesh Lines <br/>(Fat Lines) </a></li>
<li><a href="./b3dm-load/">B3DM Load (3D Tile) </a></li> <li><a href="./b3dm-load/">B3DM Load (3D Tile) </a></li>
<li><a href="./i3dm-load/">I3DM Load (3D Tile) </a></li> <li><a href="./i3dm-load/">I3DM Load (3D Tile) </a></li>
<li><a href="./pnts-load/">PNTS Load (3D Points) </a></li> <li><a href="./pnts-load/">PNTS Load (3D Points) </a></li>
<li><a href="./obj-to-glb/">Convert OBJ to GLB </a></li> <li><a href="./obj-to-glb/">Convert OBJ to GLB </a></li>
<li><a href="./3dm-to-glb/">Convert 3DM to GLB </a></li> <li><a href="./3dm-to-glb/">Convert 3DM to GLB </a></li>
<li><a href="./hdr-to-exr/">Convert HDR to EXR </a></li> <li><a href="./hdr-to-exr/">Convert HDR to EXR </a></li>
<li><a href="./fat-lines/">Fat Lines <br/>(Mesh Lines) </a></li>
</ul> </ul>
<h2 class="category">Experiments</h2> <h2 class="category">Experiments</h2>
<ul> <ul>

+ 4
- 4
package-lock.json Wyświetl plik

"version": "0.0.41", "version": "0.0.41",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.157.1005/package.tgz",
"@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.157.1006/package.tgz",
"@types/webxr": "^0.5.1", "@types/webxr": "^0.5.1",
"@types/wicg-file-system-access": "^2020.9.5", "@types/wicg-file-system-access": "^2020.9.5",
"popmotion": "^11.0.5", "popmotion": "^11.0.5",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/three": { "node_modules/@types/three": {
"version": "0.157.1005",
"resolved": "https://github.com/repalash/three-ts-types/releases/download/v0.157.1005/package.tgz",
"integrity": "sha512-Qb6XlwUyDAoNUi9IxS5lYmkShRsVFCAsG4KAXTPlmi6G2V/GIzmvXZ5Ut2SV1L+7nCEFwtvENkzP3/MVQ9cWUQ==",
"version": "0.157.1006",
"resolved": "https://github.com/repalash/three-ts-types/releases/download/v0.157.1006/package.tgz",
"integrity": "sha512-WiV0kRvPyPK3KEOGshnPDbgJzfa/YozkFSSfVu32WgFYp9hiSSVQK8wROq5We/DTWs8BmnyWuh7lzbz/4DC3jQ==",
"dependencies": { "dependencies": {
"fflate": "~0.6.10", "fflate": "~0.6.10",
"meshoptimizer": "~0.18.1" "meshoptimizer": "~0.18.1"

+ 3
- 3
package.json Wyświetl plik

"vitepress-plugin-nprogress": "^0.0.4" "vitepress-plugin-nprogress": "^0.0.4"
}, },
"dependencies": { "dependencies": {
"@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.157.1005/package.tgz",
"@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.157.1006/package.tgz",
"@types/webxr": "^0.5.1", "@types/webxr": "^0.5.1",
"@types/wicg-file-system-access": "^2020.9.5", "@types/wicg-file-system-access": "^2020.9.5",
"popmotion": "^11.0.5", "popmotion": "^11.0.5",
"ts-browser-helpers": "^0.16.2", "ts-browser-helpers": "^0.16.2",
"three": "https://github.com/repalash/three.js-modded/releases/download/v0.157.1007/package.tgz", "three": "https://github.com/repalash/three.js-modded/releases/download/v0.157.1007/package.tgz",
"three-f": "https://github.com/repalash/three.js-modded/archive/refs/tags/v0.157.1007.tar.gz", "three-f": "https://github.com/repalash/three.js-modded/archive/refs/tags/v0.157.1007.tar.gz",
"@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.157.1005/package.tgz",
"@types/three-f": "https://github.com/repalash/three-ts-types/archive/refs/tags/v0.157.1005.tar.gz",
"@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.157.1006/package.tgz",
"@types/three-f": "https://github.com/repalash/three-ts-types/archive/refs/tags/v0.157.1006.tar.gz",
"@types/three-pkg": "https://gitpkg.now.sh/repalash/three-ts-types/types/three?modded_three" "@types/three-pkg": "https://gitpkg.now.sh/repalash/three-ts-types/types/three?modded_three"
}, },
"local_dependencies": { "local_dependencies": {

+ 7
- 0
src/assetmanager/IAssetImporter.ts Wyświetl plik

* Pass a custom file to use for the import. This will be used in the importer, and nothing will be fetched from the path * Pass a custom file to use for the import. This will be used in the importer, and nothing will be fetched from the path
*/ */
importedFile?: IFile, importedFile?: IFile,

/**
* Use {@link MeshLine}(an extension of three.js `Line2`) instead of default `Line` for lines. This allows changing line width(fat lines) and other properties.
*
* Note - Only for gltf, glb files or files loaded with {@link GLTFLoader2}. If this flag is not passed, the default value is the value of the static property `GLTFLoader2.UseMeshLines`.
*/
useMeshLines?: boolean,
} }


// export type IAssetImporterEventTypes = 'onLoad' | 'onProgress' | 'onStop' | 'onError' | 'onStart' | 'loaderCreate' | 'importFile' | 'importFiles' | 'processRaw' | 'processRawStart' // export type IAssetImporterEventTypes = 'onLoad' | 'onProgress' | 'onStop' | 'onError' | 'onStart' | 'loaderCreate' | 'importFile' | 'importFiles' | 'processRaw' | 'processRawStart'

+ 87
- 7
src/assetmanager/import/GLTFLoader2.ts Wyświetl plik

import type {GLTF, GLTFLoaderPlugin, GLTFParser} from 'three/examples/jsm/loaders/GLTFLoader.js' import type {GLTF, GLTFLoaderPlugin, GLTFParser} from 'three/examples/jsm/loaders/GLTFLoader.js'
import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js' import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js'
import {LoadingManager, Object3D, OrthographicCamera, Texture} from 'three'
import {Line, LineLoop, LineSegments, LoadingManager, Object3D, OrthographicCamera, Texture} from 'three'
import {AnyOptions, safeSetProperty} from 'ts-browser-helpers' import {AnyOptions, safeSetProperty} from 'ts-browser-helpers'
import {ThreeViewer} from '../../viewer' import {ThreeViewer} from '../../viewer'
import {generateUUID} from '../../three' import {generateUUID} from '../../three'
import {ThreeSerialization} from '../../utils' import {ThreeSerialization} from '../../utils'
import { import {
DirectionalLight2, DirectionalLight2,
LineGeometry2,
LineMaterial2,
LineSegmentsGeometry2,
MeshLine,
MeshLineSegments,
PerspectiveCamera0, PerspectiveCamera0,
PhysicalMaterial, PhysicalMaterial,
PointLight2, PointLight2,
UnlitMaterial, UnlitMaterial,
} from '../../core' } from '../../core'
import {AssetImporter} from '../AssetImporter' import {AssetImporter} from '../AssetImporter'
import {ImportAddOptions} from '../AssetManager'


// todo move somewhere // todo move somewhere
const supportedEmbeddedFiles = ['hdr', 'exr', 'webp', 'avif', 'ktx', 'hdrpng', 'svg', 'cube'] // ktx2, drc handled separately
const supportedEmbeddedFiles = ['hdr', 'exr', 'webp', 'avif', 'ktx', 'hdrpng', 'svg', 'cube', 'ico', 'bmp', 'gif', 'tiff'] // ktx2, drc handled separately


export class GLTFLoader2 extends GLTFLoader implements ILoader<GLTF, Object3D|undefined> { export class GLTFLoader2 extends GLTFLoader implements ILoader<GLTF, Object3D|undefined> {
isGLTFLoader2 = true isGLTFLoader2 = true
importOptions?: ImportAddOptions

constructor(manager: LoadingManager) { constructor(manager: LoadingManager) {
super(manager) super(manager)
this.preparsers.push(glbEncryptionPreparser) this.preparsers.push(glbEncryptionPreparser)
GLTFMaterialsAlphaMapExtension.Import, GLTFMaterialsAlphaMapExtension.Import,
] ]


/**
* Use {@link MeshLine}(an extension of three.js `Line2`) instead of default `Line` for lines. This allows changing line width and other properties.
*
* This is the default value for the flag, it can also be controlled by using the `useMeshLines` in the import options.
*
* Note - Lines may not export correctly when loaded with this.
*/
static UseMeshLines = false

/** /**
* Preparsers are run on the arraybuffer/string before parsing to read the glb/gltf data * Preparsers are run on the arraybuffer/string before parsing to read the glb/gltf data
*/ */
// todo save the path of invalid textures, check if they can be found in the loaded libs, and ask the user in UI to remap it to something else manually // todo save the path of invalid textures, check if they can be found in the loaded libs, and ask the user in UI to remap it to something else manually
if (!Texture.DEFAULT_IMAGE) Texture.DEFAULT_IMAGE = AssetImporter.WHITE_IMAGE_DATA if (!Texture.DEFAULT_IMAGE) Texture.DEFAULT_IMAGE = AssetImporter.WHITE_IMAGE_DATA


const useMeshLines = this.importOptions?.useMeshLines ?? GLTFLoader2.UseMeshLines
GLTFLoader.ObjectConstructors.LineBasicMaterial = useMeshLines ? LineMaterial2 as any : UnlitLineMaterial as any

return res ? super.parse(res, path, (ret)=>{ return res ? super.parse(res, path, (ret)=>{
Texture.DEFAULT_IMAGE = val Texture.DEFAULT_IMAGE = val
GLTFLoader.ObjectConstructors.LineBasicMaterial = useMeshLines ? LineMaterial2 as any : UnlitLineMaterial as any
onLoad && onLoad(ret) onLoad && onLoad(ret)
}, onError) : onError && onError(new ErrorEvent('no data')) }, onError) : onError && onError(new ErrorEvent('no data'))
}) })
const scene: RootSceneImportResult|undefined = res ? res.scene || !!res.scenes && res.scenes.length > 0 && res.scenes[0] : undefined as any const scene: RootSceneImportResult|undefined = res ? res.scene || !!res.scenes && res.scenes.length > 0 && res.scenes[0] : undefined as any
if (!scene) return undefined if (!scene) return undefined
if (res.animations.length > 0) scene.animations = res.animations if (res.animations.length > 0) scene.animations = res.animations
scene.traverse((node: Object3D) => {
if (node.userData.gltfUUID) { // saved in GLTFExporter2
safeSetProperty(node, 'uuid', node.userData.gltfUUID, true, true)
delete node.userData.gltfUUID // have issue with cloning if we don't dispose.

const useMeshLines = this.importOptions?.useMeshLines ?? GLTFLoader2.UseMeshLines
// todo: move out and put the chosen setting in userData.
if (useMeshLines) {
const lines: Line[] = []
scene.traverse((node: Object3D) => {
if (node.userData.gltfUUID) { // saved in GLTFExporter2
safeSetProperty(node, 'uuid', node.userData.gltfUUID, true, true)
delete node.userData.gltfUUID // have issue with cloning if we don't dispose.
}
if ((node as Line).isLine) lines.push(node as Line)
})

// convert lines to mesh/fat lines
for (const line of lines) {
convertToFatLine(line)
} }
})
}

// todo: replacing lights and camera, todo: remove and change constructors in GLTFLoader.js // todo: replacing lights and camera, todo: remove and change constructors in GLTFLoader.js
if (!scene.userData) scene.userData = {} if (!scene.userData) scene.userData = {}
if (res.userData) scene.userData.gltfExtras = res.userData // todo: put back in gltf in GLTFExporter2 if (res.userData) scene.userData.gltfExtras = res.userData // todo: put back in gltf in GLTFExporter2
process(data: string | ArrayBuffer, path: string): Promise<string | ArrayBuffer> process(data: string | ArrayBuffer, path: string): Promise<string | ArrayBuffer>
[key: string]: any [key: string]: any
} }

// sample test model - https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/refs/heads/main/Models/MeshPrimitiveModes/glTF/MeshPrimitiveModes.gltf
// todo maybe do the same as others inside GLTFLoader.js
function convertToFatLine(line: Line) {
const parent = line.parent
if (!parent) {
console.warn('GLTFLoader2: Line has no parent', line)
return
}
if (line.geometry.index) line.geometry = line.geometry.toNonIndexed() // Line2 requires non indexed
const line2 =
(line as LineSegments).isLineSegments ?
new MeshLineSegments(new LineSegmentsGeometry2(), line.material as LineMaterial2) :
new MeshLine(new LineGeometry2(), line.material as LineMaterial2)
let positions = line.geometry.attributes.position.array as Float32Array
if ((line as LineLoop).isLineLoop) {
// add first pos as last.
const pos = new Float32Array(positions.length + 3)
pos.set(positions)
pos.set(positions.subarray(0, 3), positions.length)
positions = pos
}
line2.geometry.setPositions(positions)
line2.computeLineDistances()
const index = parent.children.indexOf(line)
parent.add(line2)
const {geometry, material} = line2
const ud = line.userData
line.userData = {}
line2.copy(line as any, false)
line2.geometry = geometry
line2.material = material
;[...line.children].map(c => {
line2.add(c)
})
line2.userData = {...line2.userData, ...ud}
material.userData.renderToGBuffer = false // this is set in LineMaterial2
material.userData.renderToDepth = false
line.removeFromParent()
// put at the same index
const index2 = parent.children.indexOf(line2)
if (index2 >= 0 && index2 !== index) {
parent.children.splice(index2, 1)
parent.children.splice(index, 0, line2)
}
}

+ 11
- 0
src/core/IMaterial.ts Wyświetl plik

*/ */
dispose(force?: boolean): void dispose(force?: boolean): void


/**
* Clones the Material.
* This is a shallow clone, so the properties are copied by reference.
*
* @param track - if true, the clone id and count will be tracked in the userData and a suffix will be appended to the name. default - false
*/
clone(track?: boolean): this;

// optional from subclasses, added here for autocomplete // optional from subclasses, added here for autocomplete
flatShading?: boolean flatShading?: boolean
map?: ITexture | null map?: ITexture | null


isRawShaderMaterial?: boolean isRawShaderMaterial?: boolean
isPhysicalMaterial?: boolean isPhysicalMaterial?: boolean
isLineMaterial?: boolean
isUnlitMaterial?: boolean isUnlitMaterial?: boolean
isGBufferMaterial?: boolean isGBufferMaterial?: boolean
isLineMaterial2?: boolean
isUnlitLineMaterial?: boolean


// [key: string]: any // [key: string]: any
} }

+ 1
- 0
src/core/geometry/BufferGeometry2.ts Wyświetl plik

} }


} }


+ 20
- 0
src/core/geometry/LineGeometry2.ts Wyświetl plik

import {NormalBufferAttributes, NormalOrGLBufferAttributes} from 'three'
import type {IGeometry, IGeometryEventMap, IGeometryUserData} from '../IGeometry'
import {LineGeometry} from 'three/examples/jsm/lines/LineGeometry'
import {iGeometryCommons} from './iGeometryCommons'
import type {IObject3D} from '../IObject'

export class LineGeometry2<Attributes extends NormalOrGLBufferAttributes = NormalBufferAttributes, TE extends IGeometryEventMap = IGeometryEventMap> extends LineGeometry<Attributes, TE> implements IGeometry<Attributes, TE> {
assetType: 'geometry' // dont set the value here since its checked in upgradeGeometry
center2 = iGeometryCommons.center2
setDirty = iGeometryCommons.setDirty
refreshUi = iGeometryCommons.refreshUi
appliedMeshes = new Set<IObject3D>()
declare userData: IGeometryUserData

constructor() {
super()
iGeometryCommons.upgradeGeometry.call(this)
}

}

+ 19
- 0
src/core/geometry/LineSegmentsGeometry2.ts Wyświetl plik

import {NormalBufferAttributes, NormalOrGLBufferAttributes} from 'three'
import type {IGeometry, IGeometryEventMap, IGeometryUserData} from '../IGeometry'
import {LineSegmentsGeometry} from 'three/examples/jsm/lines/LineSegmentsGeometry'
import {iGeometryCommons} from './iGeometryCommons'
import type {IObject3D} from '../IObject'

export class LineSegmentsGeometry2<Attributes extends NormalOrGLBufferAttributes = NormalBufferAttributes, TE extends IGeometryEventMap = IGeometryEventMap> extends LineSegmentsGeometry<Attributes, TE> implements IGeometry<Attributes, TE> {
assetType: 'geometry' // dont set the value here since its checked in upgradeGeometry
center2 = iGeometryCommons.center2
setDirty = iGeometryCommons.setDirty
refreshUi = iGeometryCommons.refreshUi
appliedMeshes = new Set<IObject3D>()
declare userData: IGeometryUserData

constructor() {
super()
iGeometryCommons.upgradeGeometry.call(this)
}
}

+ 23
- 0
src/core/geometry/WireframeGeometry3.ts Wyświetl plik

import {
BufferGeometry,
NormalBufferAttributes,
NormalOrGLBufferAttributes,
} from 'three'
import type {IGeometry, IGeometryEventMap, IGeometryUserData} from '../IGeometry'
import {WireframeGeometry2} from 'three/examples/jsm/lines/WireframeGeometry2'
import {iGeometryCommons} from './iGeometryCommons'
import type {IObject3D} from '../IObject'

export class WireframeGeometry3<Attributes extends NormalOrGLBufferAttributes = NormalBufferAttributes, TE extends IGeometryEventMap = IGeometryEventMap> extends WireframeGeometry2<Attributes, TE> implements IGeometry<Attributes, TE> {
assetType: 'geometry' // dont set the value here since its checked in upgradeGeometry
center2 = iGeometryCommons.center2
setDirty = iGeometryCommons.setDirty
refreshUi = iGeometryCommons.refreshUi
appliedMeshes = new Set<IObject3D>()
declare userData: IGeometryUserData

constructor(geometry: BufferGeometry) {
super(geometry)
iGeometryCommons.upgradeGeometry.call(this)
}
}

+ 5
- 0
src/core/index.ts Wyświetl plik

export {LineMaterial2} from './material/LineMaterial2' export {LineMaterial2} from './material/LineMaterial2'
export {LegacyPhongMaterial} from './material/LegacyPhongMaterial' export {LegacyPhongMaterial} from './material/LegacyPhongMaterial'
export {Mesh2} from './object/Mesh2' export {Mesh2} from './object/Mesh2'
export {MeshLine} from './object/MeshLine'
export {MeshLineSegments} from './object/MeshLineSegments'
export {BufferGeometry2} from './geometry/BufferGeometry2' export {BufferGeometry2} from './geometry/BufferGeometry2'
export {LineGeometry2} from './geometry/LineGeometry2'
export {LineSegmentsGeometry2} from './geometry/LineSegmentsGeometry2'
export {WireframeGeometry3} from './geometry/WireframeGeometry3'
export {AmbientLight2} from './light/AmbientLight2' export {AmbientLight2} from './light/AmbientLight2'
export {DirectionalLight2} from './light/DirectionalLight2' export {DirectionalLight2} from './light/DirectionalLight2'
export {HemisphereLight2} from './light/HemisphereLight2' export {HemisphereLight2} from './light/HemisphereLight2'

+ 1
- 1
src/core/material/LegacyPhongMaterial.ts Wyświetl plik

readonly appliedMeshes: Set<IObject3D> = new Set() readonly appliedMeshes: Set<IObject3D> = new Set()
readonly setDirty = iMaterialCommons.setDirty readonly setDirty = iMaterialCommons.setDirty
dispose(): this {return iMaterialCommons.dispose(super.dispose).call(this)} dispose(): this {return iMaterialCommons.dispose(super.dispose).call(this)}
clone(): this {return iMaterialCommons.clone(super.clone).call(this)}
clone(track = false): this {return iMaterialCommons.clone(super.clone).call(this, track)}
dispatchEvent<T extends Extract<keyof (TE&IMaterialEventMap), string>>(event: BaseEvent<T> & (TE&IMaterialEventMap)[T]): void {iMaterialCommons.dispatchEvent(super.dispatchEvent).call(this, event)} dispatchEvent<T extends Extract<keyof (TE&IMaterialEventMap), string>>(event: BaseEvent<T> & (TE&IMaterialEventMap)[T]): void {iMaterialCommons.dispatchEvent(super.dispatchEvent).call(this, event)}
generator?: IMaterialGenerator generator?: IMaterialGenerator



+ 45
- 5
src/core/material/LineMaterial2.ts Wyświetl plik

import {generateUiConfig, uiColor, uiInput, uiNumber, UiObjectConfig, uiToggle, uiVector} from 'uiconfig.js' import {generateUiConfig, uiColor, uiInput, uiNumber, UiObjectConfig, uiToggle, uiVector} from 'uiconfig.js'
import {BaseEvent, Color, IUniform, Material, Shader, Vector2, WebGLRenderer} from 'three'
import {
BaseEvent,
BufferGeometry,
Camera,
Color,
IUniform,
Material,
Object3D,
Scene,
Shader,
Vector2,
WebGLRenderer,
} from 'three'
import {SerializationMetaType, shaderReplaceString, ThreeSerialization} from '../../utils' import {SerializationMetaType, shaderReplaceString, ThreeSerialization} from '../../utils'
import {IMaterial, IMaterialEventMap, IMaterialGenerator, IMaterialParameters, IMaterialTemplate} from '../IMaterial'
import {
IMaterial,
IMaterialEventMap,
IMaterialGenerator,
IMaterialParameters,
IMaterialTemplate,
IMaterialUserData,
} from '../IMaterial'
import {MaterialExtension} from '../../materials' import {MaterialExtension} from '../../materials'
import {iMaterialCommons, threeMaterialPropList} from './iMaterialCommons' import {iMaterialCommons, threeMaterialPropList} from './iMaterialCommons'
import {IObject3D} from '../IObject' import {IObject3D} from '../IObject'
public static readonly TYPE = 'LineMaterial2' // not using .type because it is used by three.js public static readonly TYPE = 'LineMaterial2' // not using .type because it is used by three.js
assetType = 'material' as const assetType = 'material' as const


declare userData: IMaterialUserData

public readonly isLineMaterial2 = true public readonly isLineMaterial2 = true


readonly appliedMeshes: Set<IObject3D> = new Set() readonly appliedMeshes: Set<IObject3D> = new Set()
readonly setDirty = iMaterialCommons.setDirty readonly setDirty = iMaterialCommons.setDirty
dispose(): this {return iMaterialCommons.dispose(super.dispose).call(this)} dispose(): this {return iMaterialCommons.dispose(super.dispose).call(this)}
clone(): this {return iMaterialCommons.clone(super.clone).call(this)}
clone(track = false): this {return iMaterialCommons.clone(super.clone).call(this, track)}
dispatchEvent<T extends Extract<keyof (TE&IMaterialEventMap), string>>(event: BaseEvent<T> & (TE&IMaterialEventMap)[T]): void {iMaterialCommons.dispatchEvent(super.dispatchEvent).call(this, event)} dispatchEvent<T extends Extract<keyof (TE&IMaterialEventMap), string>>(event: BaseEvent<T> & (TE&IMaterialEventMap)[T]): void {iMaterialCommons.dispatchEvent(super.dispatchEvent).call(this, event)}


generator?: IMaterialGenerator generator?: IMaterialGenerator
if (customMaterialExtensions) this.registerMaterialExtensions(customMaterialExtensions) if (customMaterialExtensions) this.registerMaterialExtensions(customMaterialExtensions)
iMaterialCommons.upgradeMaterial.call(this) iMaterialCommons.upgradeMaterial.call(this)
this.setValues(parameters) this.setValues(parameters)
// this.userData.renderToGBuffer = false
// this.userData.renderToDepth = false
} }


// region Material Extension // region Material Extension
super.onBeforeCompile(shader, renderer) super.onBeforeCompile(shader, renderer)
} }


onAfterRender = iMaterialCommons.onAfterRenderOverride(super.onAfterRender)
autoUpdateResolution = true

onBeforeRender(renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D): void {
if (this.autoUpdateResolution) renderer.getSize(this.resolution)
super.onBeforeRender(renderer, scene, camera, geometry, object)
iMaterialCommons.onBeforeRender.call(this, renderer, scene, camera, geometry, object)
}

onAfterRender(renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D): void {
super.onAfterRender(renderer, scene, camera, geometry, object)
iMaterialCommons.onAfterRender.call(this, renderer, scene, camera, geometry, object)
}


// endregion // endregion


expanded: true, expanded: true,
onChange: (ev)=>{ onChange: (ev)=>{
if (!ev.config || ev.config.onChange) return if (!ev.config || ev.config.onChange) return
// this.uniformsNeedUpdate = true
// this.appliedMeshes.forEach(m=>{
// if ((m.isLineSegments2 || m.isLineSegments) && m.computeLineDistances) {
// m.computeLineDistances()
// }
// })
// todo set needsUpdate true only for properties that require it like maps. // todo set needsUpdate true only for properties that require it like maps.
this.setDirty({uiChangeEvent: ev, needsUpdate: !!ev.last, refreshUi: !!ev.last}) this.setDirty({uiChangeEvent: ev, needsUpdate: !!ev.last, refreshUi: !!ev.last})
}, },
*/ */
setValues(parameters: Material|(LineMaterialParameters&{type?:string}), allowInvalidType = true, clearCurrentUserData: boolean|undefined = undefined): this { setValues(parameters: Material|(LineMaterialParameters&{type?:string}), allowInvalidType = true, clearCurrentUserData: boolean|undefined = undefined): this {
if (!parameters) return this if (!parameters) return this
if (parameters.type && !allowInvalidType && !['LineMaterial', this.constructor.TYPE].includes(parameters.type)) {
if (parameters.type && !allowInvalidType && !['LineMaterial', this.constructor.TYPE].includes(parameters.type) && !(parameters as LineMaterial2).isLineMaterial && !(parameters as LineMaterial2).isLineMaterial2) {
console.error('Material type is not supported:', parameters.type) console.error('Material type is not supported:', parameters.type)
return this return this
} }

+ 2
- 2
src/core/material/ObjectShaderMaterial.ts Wyświetl plik

readonly appliedMeshes: Set<IObject3D> = new Set() readonly appliedMeshes: Set<IObject3D> = new Set()
readonly setDirty = iMaterialCommons.setDirty readonly setDirty = iMaterialCommons.setDirty
dispose(): this {return iMaterialCommons.dispose(super.dispose).call(this)} dispose(): this {return iMaterialCommons.dispose(super.dispose).call(this)}
clone(): this {return iMaterialCommons.clone(super.clone).call(this)}
clone(track = false): this {return iMaterialCommons.clone(super.clone).call(this, track)}
dispatchEvent<T extends Extract<keyof (TE&IMaterialEventMap), string>>(event: BaseEvent<T> & (TE&IMaterialEventMap)[T]): void {iMaterialCommons.dispatchEvent(super.dispatchEvent).call(this, event)} dispatchEvent<T extends Extract<keyof (TE&IMaterialEventMap), string>>(event: BaseEvent<T> & (TE&IMaterialEventMap)[T]): void {iMaterialCommons.dispatchEvent(super.dispatchEvent).call(this, event)}


generator?: IMaterialGenerator generator?: IMaterialGenerator
} }
} }


// todo gltf material extension
// todo gltf material extension

+ 1
- 1
src/core/material/PhysicalMaterial.ts Wyświetl plik

readonly appliedMeshes: Set<IObject3D> = new Set() readonly appliedMeshes: Set<IObject3D> = new Set()
readonly setDirty = iMaterialCommons.setDirty readonly setDirty = iMaterialCommons.setDirty
dispose(): this {return iMaterialCommons.dispose(super.dispose).call(this)} dispose(): this {return iMaterialCommons.dispose(super.dispose).call(this)}
clone(): this {return iMaterialCommons.clone(super.clone).call(this)}
clone(track = false): this {return iMaterialCommons.clone(super.clone).call(this, track)}
dispatchEvent<T extends Extract<keyof (TE&IMaterialEventMap), string>>(event: BaseEvent<T> & (TE&IMaterialEventMap)[T]): void {iMaterialCommons.dispatchEvent(super.dispatchEvent).call(this, event)} dispatchEvent<T extends Extract<keyof (TE&IMaterialEventMap), string>>(event: BaseEvent<T> & (TE&IMaterialEventMap)[T]): void {iMaterialCommons.dispatchEvent(super.dispatchEvent).call(this, event)}


generator?: IMaterialGenerator generator?: IMaterialGenerator

+ 1
- 1
src/core/material/ShaderMaterial2.ts Wyświetl plik

readonly appliedMeshes: Set<any> = new Set() readonly appliedMeshes: Set<any> = new Set()
readonly setDirty = iMaterialCommons.setDirty readonly setDirty = iMaterialCommons.setDirty
dispose(): this {return iMaterialCommons.dispose(super.dispose).call(this)} dispose(): this {return iMaterialCommons.dispose(super.dispose).call(this)}
clone(): this {return iMaterialCommons.clone(super.clone).call(this)}
clone(track = false): this {return iMaterialCommons.clone(super.clone).call(this, track)}
dispatchEvent<T extends Extract<keyof (TE&IMaterialEventMap), string>>(event: BaseEvent<T> & (TE&IMaterialEventMap)[T]): void {iMaterialCommons.dispatchEvent(super.dispatchEvent).call(this, event)} dispatchEvent<T extends Extract<keyof (TE&IMaterialEventMap), string>>(event: BaseEvent<T> & (TE&IMaterialEventMap)[T]): void {iMaterialCommons.dispatchEvent(super.dispatchEvent).call(this, event)}


readonly isRawShaderMaterial: boolean readonly isRawShaderMaterial: boolean

+ 1
- 1
src/core/material/UnlitLineMaterial.ts Wyświetl plik

readonly appliedMeshes: Set<IObject3D> = new Set() readonly appliedMeshes: Set<IObject3D> = new Set()
readonly setDirty = iMaterialCommons.setDirty readonly setDirty = iMaterialCommons.setDirty
dispose(): this {return iMaterialCommons.dispose(super.dispose).call(this)} dispose(): this {return iMaterialCommons.dispose(super.dispose).call(this)}
clone(): this {return iMaterialCommons.clone(super.clone).call(this)}
clone(track = false): this {return iMaterialCommons.clone(super.clone).call(this, track)}
dispatchEvent<T extends Extract<keyof (TE&IMaterialEventMap), string>>(event: BaseEvent<T> & (TE&IMaterialEventMap)[T]): void {iMaterialCommons.dispatchEvent(super.dispatchEvent).call(this, event)} dispatchEvent<T extends Extract<keyof (TE&IMaterialEventMap), string>>(event: BaseEvent<T> & (TE&IMaterialEventMap)[T]): void {iMaterialCommons.dispatchEvent(super.dispatchEvent).call(this, event)}


generator?: IMaterialGenerator generator?: IMaterialGenerator

+ 1
- 1
src/core/material/UnlitMaterial.ts Wyświetl plik

readonly appliedMeshes: Set<IObject3D> = new Set() readonly appliedMeshes: Set<IObject3D> = new Set()
readonly setDirty = iMaterialCommons.setDirty readonly setDirty = iMaterialCommons.setDirty
dispose(): this {return iMaterialCommons.dispose(super.dispose).call(this)} dispose(): this {return iMaterialCommons.dispose(super.dispose).call(this)}
clone(): this {return iMaterialCommons.clone(super.clone).call(this)}
clone(track = false): this {return iMaterialCommons.clone(super.clone).call(this, track)}
dispatchEvent<T extends Extract<keyof (TE&IMaterialEventMap), string>>(event: BaseEvent<T> & (TE&IMaterialEventMap)[T]): void {iMaterialCommons.dispatchEvent(super.dispatchEvent).call(this, event)} dispatchEvent<T extends Extract<keyof (TE&IMaterialEventMap), string>>(event: BaseEvent<T> & (TE&IMaterialEventMap)[T]): void {iMaterialCommons.dispatchEvent(super.dispatchEvent).call(this, event)}


generator?: IMaterialGenerator generator?: IMaterialGenerator

+ 15
- 10
src/core/material/iMaterialCommons.ts Wyświetl plik

superDispose.call(this) superDispose.call(this)
}, },
clone: (superClone: Material['clone']): IMaterial['clone'] => clone: (superClone: Material['clone']): IMaterial['clone'] =>
function(this: IMaterial): IMaterial {
if (!this.userData.cloneId) {
this.userData.cloneId = '0'
}
if (!this.userData.cloneCount) {
this.userData.cloneCount = 0
function(this: IMaterial, track = false): IMaterial {
if (track) {
if (!this.userData.cloneId) {
this.userData.cloneId = '0'
}
if (!this.userData.cloneCount) {
this.userData.cloneCount = 0
}
this.userData.cloneCount += 1
} }
this.userData.cloneCount += 1


const material: IMaterial = this.generator?.({})?.setValues(this, false) ?? superClone.call(this) const material: IMaterial = this.generator?.({})?.setValues(this, false) ?? superClone.call(this)


material.userData.cloneId = material.userData.cloneId + '_' + this.userData.cloneCount
material.userData.cloneCount = 0
material.name = material.name + '_' + material.userData.cloneId
if (track) {

material.userData.cloneId = material.userData.cloneId + '_' + this.userData.cloneCount
material.userData.cloneCount = 0
material.name = (material.name || 'mat') + '_' + material.userData.cloneId
}


return material return material
}, },

+ 48
- 8
src/core/object/IObjectUi.ts Wyświetl plik

import {ThreeViewer} from '../../viewer' import {ThreeViewer} from '../../viewer'
import {UnlitMaterial} from '../material/UnlitMaterial' import {UnlitMaterial} from '../material/UnlitMaterial'
import {PhysicalMaterial} from '../material/PhysicalMaterial' import {PhysicalMaterial} from '../material/PhysicalMaterial'
import {LineMaterial2} from '../material/LineMaterial2'
import {UnlitLineMaterial} from '../material/UnlitLineMaterial'
import {IMaterial} from '../IMaterial'


// todo move somewhere? // todo move somewhere?
const defaultMaterial = new UnlitMaterial() const defaultMaterial = new UnlitMaterial()
defaultMaterial.name = 'Default Unlit Material' defaultMaterial.name = 'Default Unlit Material'
defaultMaterial.uiConfig = undefined as any defaultMaterial.uiConfig = undefined as any
const defaultUnlitLineMaterial = new UnlitLineMaterial()
defaultUnlitLineMaterial.name = 'Default Unlit Line Material'
defaultUnlitLineMaterial.uiConfig = undefined as any
const defaultLineMaterial = new LineMaterial2()
defaultLineMaterial.name = 'Default Line Material'
defaultLineMaterial.uiConfig = undefined as any


export function makeICameraCommonUiConfig(this: ICamera, config: UiObjectConfig): UiObjectConfig[] { export function makeICameraCommonUiConfig(this: ICamera, config: UiObjectConfig): UiObjectConfig[] {
return [ return [
type: 'checkbox', type: 'checkbox',
label: 'Visible', label: 'Visible',
property: [this, 'visible'], property: [this, 'visible'],
onChange: (e)=>{
this.setDirty?.({uiChangeEvent: e, refreshScene: true, refreshUi: true, change: 'visible'})
},
}, },
{ {
type: 'button', type: 'button',
type: 'input', type: 'input',
label: 'Name', label: 'Name',
property: [this, 'name'], property: [this, 'name'],
onChange: (e: any)=>{
if (e.last) this.setDirty?.({refreshScene: true, frameFade: false, refreshUi: true})
onChange: (e)=>{
if (e.last) this.setDirty?.({uiChangeEvent: e, refreshScene: true, frameFade: false, refreshUi: true})
}, },
}, },
{ {
type: 'checkbox', type: 'checkbox',
label: 'Casts Shadow', label: 'Casts Shadow',
hidden: () => !(this as any).isMesh,
hidden: () => !this.isMesh,
property: [this, 'castShadow'], property: [this, 'castShadow'],
onChange: (e)=>{
this.setDirty?.({uiChangeEvent: e, refreshScene: true, refreshUi: true, change: 'castShadow'})
},
}, },
{ {
type: 'checkbox', type: 'checkbox',
label: 'Receive Shadow', label: 'Receive Shadow',
hidden: () => !(this as any).isMesh,
hidden: () => !this.isMesh,
property: [this, 'receiveShadow'], property: [this, 'receiveShadow'],
onChange: (e)=>{
this.setDirty?.({uiChangeEvent: e, refreshScene: true, refreshUi: true, change: 'receiveShadow'})
},
}, },
{ {
type: 'checkbox', type: 'checkbox',
{ {
label: 'Remove Material(s)', label: 'Remove Material(s)',
type: 'button', type: 'button',
hidden: ()=>!this.materials?.length || this.materials.length === 1 && this.materials[0] === defaultMaterial,
hidden: ()=>!this.materials?.length || this.materials.length === 1 && (<IMaterial[]>[defaultMaterial, defaultLineMaterial, defaultUnlitLineMaterial]).includes(this.materials[0]),
value: ()=>{
const mat = this.materials
this.material = this.isLineSegments2 ?
[defaultLineMaterial] : this.isLineSegments ?
[defaultUnlitLineMaterial] : [defaultMaterial]
return ()=> this.material = mat
},
},
{
label: 'New Line Material',
type: 'button',
hidden: ()=>!this.isLineSegments2 || !(!this.materials?.length || this.materials.length === 1 && this.materials[0] === defaultLineMaterial),
value: ()=>{
const mat = this.materials
this.material = [new LineMaterial2()]
return ()=> this.material = mat
},
},
{
label: 'New Unlit Line Material',
type: 'button',
hidden: ()=>!this.isLineSegments || !(!this.materials?.length || this.materials.length === 1 && this.materials[0] === defaultUnlitLineMaterial),
value: ()=>{ value: ()=>{
const mat = this.materials const mat = this.materials
this.material = [defaultMaterial]
this.material = [new UnlitLineMaterial()]
return ()=> this.material = mat return ()=> this.material = mat
}, },
}, },
{ {
label: 'New Physical Material', label: 'New Physical Material',
type: 'button', type: 'button',
hidden: ()=>!(!this.materials?.length || this.materials.length === 1 && this.materials[0] === defaultMaterial),
hidden: ()=>!(!this.materials?.length || this.materials.length === 1 && this.materials[0] === defaultMaterial) || !!this.isLineSegments2 || !!this.isLineSegments,
value: ()=>{ value: ()=>{
const mat = this.materials const mat = this.materials
this.material = [new PhysicalMaterial()] this.material = [new PhysicalMaterial()]
{ {
label: 'New Unlit Material', label: 'New Unlit Material',
type: 'button', type: 'button',
hidden: ()=>!(!this.materials?.length || this.materials.length === 1 && this.materials[0] === defaultMaterial),
hidden: ()=>!(!this.materials?.length || this.materials.length === 1 && this.materials[0] === defaultMaterial) || !!this.isLineSegments2 || !!this.isLineSegments,
value: ()=>{ value: ()=>{
const mat = this.materials const mat = this.materials
this.material = [new UnlitMaterial()] this.material = [new UnlitMaterial()]

+ 1
- 0
src/core/object/Mesh2.ts Wyświetl plik

// endregion // endregion


} }


+ 55
- 0
src/core/object/MeshLine.ts Wyświetl plik

import {LineGeometry2} from '../geometry/LineGeometry2'
import {LineMaterial2} from '../material/LineMaterial2'
import {IObject3D, IObject3DEventMap, IObject3DUserData} from '../IObject'
import {Line2} from 'three/examples/jsm/lines/Line2'
import {iObjectCommons} from './iObjectCommons'
import {IMaterial} from '../IMaterial'

export class MeshLine<
TGeometry extends LineGeometry2 = LineGeometry2,
TMaterial extends LineMaterial2 = LineMaterial2,
TE extends IObject3DEventMap = IObject3DEventMap
> extends Line2<TGeometry, TMaterial, TE> implements IObject3D<TE> {
assetType = 'model' as const
setDirty = iObjectCommons.setDirty
refreshUi = iObjectCommons.refreshUi
public readonly isMeshLine = true

declare material: TMaterial
declare readonly materials: IMaterial[]
declare geometry: TGeometry

/**
* @deprecated use `this` instead
*/
get modelObject(): this {
return this
}

constructor(geometry?: TGeometry, material?: TMaterial) {
super(geometry, material)
iObjectCommons.upgradeObject3D.call(this)
}

declare userData: IObject3DUserData

// region inherited type fixes
// re-declaring from IObject3D because: https://github.com/microsoft/TypeScript/issues/16936

traverse: (callback: (object: IObject3D) => void) => void
traverseVisible: (callback: (object: IObject3D) => void) => void
traverseAncestors: (callback: (object: IObject3D) => void) => void
getObjectById: <T extends IObject3D = IObject3D>(id: number) => T | undefined
getObjectByName: <T extends IObject3D = IObject3D>(name: string) => T | undefined
getObjectByProperty: <T extends IObject3D = IObject3D>(name: string, value: string) => T | undefined
copy: (source: MeshLine | IObject3D, recursive?: boolean, ...args: any[]) => this
clone: (recursive?: boolean) => this
remove: (...object: IObject3D[]) => this
declare parent: IObject3D | null
declare children: IObject3D[]
dispose: (removeFromParent?: boolean) => void

// endregion

}


+ 55
- 0
src/core/object/MeshLineSegments.ts Wyświetl plik

import {LineSegmentsGeometry2} from '../geometry/LineSegmentsGeometry2'
import {LineMaterial2} from '../material/LineMaterial2'
import {IObject3D, IObject3DEventMap, IObject3DUserData} from '../IObject'
import {LineSegments2} from 'three/examples/jsm/lines/LineSegments2'
import {iObjectCommons} from './iObjectCommons'
import {IMaterial} from '../IMaterial'
import {MeshLine} from './MeshLine'

export class MeshLineSegments<
TGeometry extends LineSegmentsGeometry2 = LineSegmentsGeometry2,
TMaterial extends LineMaterial2= LineMaterial2,
TE extends IObject3DEventMap = IObject3DEventMap
> extends LineSegments2<TGeometry, TMaterial, TE> implements IObject3D<TE> {
assetType = 'model' as const
setDirty = iObjectCommons.setDirty
refreshUi = iObjectCommons.refreshUi
public readonly isMeshLineSegments = true

declare material: TMaterial
declare readonly materials: IMaterial[]
declare geometry: TGeometry

/**
* @deprecated use `this` instead
*/
get modelObject(): this {
return this
}

constructor(geometry?: TGeometry, material?: TMaterial) {
super(geometry, material)
iObjectCommons.upgradeObject3D.call(this)
}

declare userData: IObject3DUserData

// region inherited type fixes
// re-declaring from IObject3D because: https://github.com/microsoft/TypeScript/issues/16936

traverse: (callback: (object: IObject3D) => void) => void
traverseVisible: (callback: (object: IObject3D) => void) => void
traverseAncestors: (callback: (object: IObject3D) => void) => void
getObjectById: <T extends IObject3D = IObject3D>(id: number) => T | undefined
getObjectByName: <T extends IObject3D = IObject3D>(name: string) => T | undefined
getObjectByProperty: <T extends IObject3D = IObject3D>(name: string, value: string) => T | undefined
copy: (source: MeshLine | IObject3D, recursive?: boolean, ...args: any[]) => this
clone: (recursive?: boolean) => this
remove: (...object: IObject3D[]) => this
declare parent: IObject3D | null
declare children: IObject3D[]
dispose: (removeFromParent?: boolean) => void

// endregion

}

+ 8
- 2
src/three/Threejs.ts Wyświetl plik

export type {GLTFLoaderPlugin, GLTF, GLTFReference, GLTFReferenceType} from 'three/examples/jsm/loaders/GLTFLoader' export type {GLTFLoaderPlugin, GLTF, GLTFReference, GLTFReferenceType} from 'three/examples/jsm/loaders/GLTFLoader'
export {GLTFParser, GLTFBinaryExtension, GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader' export {GLTFParser, GLTFBinaryExtension, GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader'
export {GLTFExporter} from 'three/examples/jsm/exporters/GLTFExporter' export {GLTFExporter} from 'three/examples/jsm/exporters/GLTFExporter'

export {LineSegments2} from 'three/examples/jsm/lines/LineSegments2.js'
export {Line2} from 'three/examples/jsm/lines/Line2.js'
export {LineSegmentsGeometry} from 'three/examples/jsm/lines/LineSegmentsGeometry.js'
export {LineGeometry} from 'three/examples/jsm/lines/LineGeometry.js'
export {LineMaterial} from 'three/examples/jsm/lines/LineMaterial.js'
export {Wireframe} from 'three/examples/jsm/lines/Wireframe.js'
export {WireframeGeometry2} from 'three/examples/jsm/lines/WireframeGeometry2.js'
export {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js' export {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js'
export * from 'three/examples/jsm/utils/BufferGeometryUtils.js' export * from 'three/examples/jsm/utils/BufferGeometryUtils.js'
export type {Event, EventListener, EventListener2} from 'three'
export type {Event, EventListener, EventListener2, Event2} from 'three'
export type {MeshPhysicalMaterialParameters, MeshBasicMaterialParameters, MaterialParameters} from 'three' export type {MeshPhysicalMaterialParameters, MeshBasicMaterialParameters, MaterialParameters} from 'three'

+ 0
- 1
src/three/controls/OrbitControls3.ts Wyświetl plik



throttleUpdate = 60 // throttle to 60 updates per second (implemented in OrbitControls.js.update() method) throttleUpdate = 60 // throttle to 60 updates per second (implemented in OrbitControls.js.update() method)


// todo add to three-ts-types
stopDamping!: () => void stopDamping!: () => void
} }

+ 24
- 7
src/three/math/Box3B.ts Wyświetl plik

import {Box2, Box3, BufferAttribute, Camera, InterleavedBufferAttribute, Mesh, Object3D, Vector3} from 'three'
import {
Box2,
Box3,
BufferAttribute,
BufferGeometry,
Camera,
InterleavedBufferAttribute,
Mesh,
Object3D,
Vector3,
} from 'three'
import type {IObject3D} from '../../core' import type {IObject3D} from '../../core'


export class Box3B extends Box3 { export class Box3B extends Box3 {
if (!object.visible && ignoreInvisible) return this if (!object.visible && ignoreInvisible) return this
if (ignoreObject && ignoreObject(object)) return this if (ignoreObject && ignoreObject(object)) return this


// copied the whole function from three.js to pass in ignoreInvisible
// copied the whole function from three.js to pass in ignoreInvisible, support precise


// Computes the world-axis-aligned bounding box of an object (including its children), // Computes the world-axis-aligned bounding box of an object (including its children),
// accounting for both the object's, and children's, world transforms // accounting for both the object's, and children's, world transforms
// InstancedMesh has boundingBox = null, so it can be computed // InstancedMesh has boundingBox = null, so it can be computed
if ((object as IObject3D).boundingBox !== undefined) { if ((object as IObject3D).boundingBox !== undefined) {


if ((object as IObject3D).boundingBox === null && typeof (object as IObject3D).computeBoundingBox === 'function') {
if (/* (object as IObject3D).boundingBox === null && */typeof (object as IObject3D).computeBoundingBox === 'function') {


(object as IObject3D).computeBoundingBox!() (object as IObject3D).computeBoundingBox!()


const geometry = (object as Mesh).geometry const geometry = (object as Mesh).geometry


if (geometry !== undefined) { if (geometry !== undefined) {
if (precise && geometry.attributes != undefined && geometry.attributes.position !== undefined) {
// checking for computeBoundingBox to support when overridden in subclass.
if (precise && geometry.attributes != undefined && geometry.attributes.position !== undefined && Object.getPrototypeOf(geometry).computeBoundingBox === BufferGeometry.prototype.computeBoundingBox) {
// in case of precise, apply the matrix to positions before expanding the box
// todo add precise option to computeBoundingBox
const position = geometry.attributes.position as any as BufferAttribute | InterleavedBufferAttribute const position = geometry.attributes.position as any as BufferAttribute | InterleavedBufferAttribute
for (let i = 0, l = position.count; i < l; i++) { for (let i = 0, l = position.count; i < l; i++) {
this._vector.fromBufferAttribute(position, i).applyMatrix4(object.matrixWorld) this._vector.fromBufferAttribute(position, i).applyMatrix4(object.matrixWorld)
} else { } else {
if (geometry.boundingBox === null) if (geometry.boundingBox === null)
geometry.computeBoundingBox() geometry.computeBoundingBox()
Box3B._box.copy(geometry.boundingBox!)
Box3B._box.applyMatrix4(object.matrixWorld)
if (geometry.boundingBox) {
Box3B._box.copy(geometry.boundingBox)
Box3B._box.applyMatrix4(object.matrixWorld)


this.union(Box3B._box)
this.union(Box3B._box)
} else {
console.warn('Box3B - Unable to compute bounds for', object, geometry)
}


} }
} }

+ 4
- 1
src/three/widgets/BoxSelectionWidget.ts Wyświetl plik

color: '#ff2222' as any, transparent: true, opacity: 0.9, color: '#ff2222' as any, transparent: true, opacity: 0.9,
linewidth: 5, // in pixels linewidth: 5, // in pixels
resolution: new Vector2(1024, 1024), // to be set by renderer, eventually resolution: new Vector2(1024, 1024), // to be set by renderer, eventually
worldUnits: false,
dashed: false, dashed: false,
toneMapped: false, toneMapped: false,
}) })
matLine.userData.renderToGBuffer = false
matLine.userData.renderToDepth = false
this.lineMaterial = matLine this.lineMaterial = matLine


const ls = new LineSegmentsGeometry() const ls = new LineSegmentsGeometry()
if (selected) { if (selected) {
const bbox = new Box3B().expandByObject(selected, false) const bbox = new Box3B().expandByObject(selected, false)
// const scale = bbox.getBoundingSphere(new Sphere()).radius // const scale = bbox.getBoundingSphere(new Sphere()).radius
bbox.getSize(this.scale).multiplyScalar(this.boundingScaleMultiplier).clampScalar(0.1, 100)
bbox.getSize(this.scale).multiplyScalar(this.boundingScaleMultiplier).clampScalar(0.1, 1e8)
this.setVisible(true) this.setVisible(true)
} }
} }

+ 2
- 0
src/three/widgets/CameraHelper2.ts Wyświetl plik

depthTest: false, depthTest: false,
depthWrite: false, depthWrite: false,
}) })
material.userData.renderToGBuffer = false
material.userData.renderToDepth = false


const {vertices, colors, pointMap} = generateVertices() const {vertices, colors, pointMap} = generateVertices()



+ 2
- 0
src/three/widgets/DirectionalLightHelper2.ts Wyświetl plik

depthTest: false, depthTest: false,
depthWrite: false, depthWrite: false,
}) })
this.material.userData.renderToGBuffer = false
this.material.userData.renderToDepth = false


this.lightPlane = new Line2(geometry, this.material) this.lightPlane = new Line2(geometry, this.material)
this.add(this.lightPlane) this.add(this.lightPlane)

+ 2
- 0
src/three/widgets/PointLightHelper2.ts Wyświetl plik

depthTest: false, depthTest: false,
depthWrite: false, depthWrite: false,
}) })
this.material.userData.renderToGBuffer = false
this.material.userData.renderToDepth = false


this.lightSphere = new Wireframe(geometry, this.material) this.lightSphere = new Wireframe(geometry, this.material)
this.lightSphere.computeLineDistances() this.lightSphere.computeLineDistances()

+ 3
- 5
src/three/widgets/SpotLightHelper2.ts Wyświetl plik

import {ColorRepresentation, Object3D, SpotLight, Vector3} from 'three' import {ColorRepresentation, Object3D, SpotLight, Vector3} from 'three'
import {LineGeometry} from 'three/examples/jsm/lines/LineGeometry.js'
import {LineSegments2} from 'three/examples/jsm/lines/LineSegments2.js' import {LineSegments2} from 'three/examples/jsm/lines/LineSegments2.js'
import {LineSegmentsGeometry} from 'three/examples/jsm/lines/LineSegmentsGeometry.js' import {LineSegmentsGeometry} from 'three/examples/jsm/lines/LineSegmentsGeometry.js'
import {onChange} from 'ts-browser-helpers' import {onChange} from 'ts-browser-helpers'


if (size === undefined) size = 0.5 if (size === undefined) size = 0.5


let geometry = new LineSegmentsGeometry()
const geometry = new LineSegmentsGeometry()
const positions = [ const positions = [
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1,
0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1,
depthTest: false, depthTest: false,
depthWrite: false, depthWrite: false,
}) })
this.material.userData.renderToGBuffer = false
this.material.userData.renderToDepth = false


this.cone = new LineSegments2(geometry, this.material) this.cone = new LineSegments2(geometry, this.material)
this.add(this.cone) this.add(this.cone)


geometry = new LineGeometry()
geometry.setPositions([0, 0, 0, 0, 0, 1])

this.update() this.update()


this.traverse(o => { this.traverse(o => {

Ładowanie…
Anuluj
Zapisz