| @@ -0,0 +1,36 @@ | |||
| <!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> | |||
| @@ -0,0 +1,55 @@ | |||
| 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) | |||
| @@ -0,0 +1,36 @@ | |||
| <!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> | |||
| @@ -0,0 +1,84 @@ | |||
| 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) | |||
| @@ -440,6 +440,7 @@ | |||
| <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="./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="./i3dm-load/">I3DM Load (3D Tile) </a></li> | |||
| <li><a href="./pnts-load/">PNTS Load (3D Points) </a></li> | |||
| @@ -504,6 +505,7 @@ | |||
| <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="./hdr-to-exr/">Convert HDR to EXR </a></li> | |||
| <li><a href="./fat-lines/">Fat Lines <br/>(Mesh Lines) </a></li> | |||
| </ul> | |||
| <h2 class="category">Experiments</h2> | |||
| <ul> | |||
| @@ -9,7 +9,7 @@ | |||
| "version": "0.0.41", | |||
| "license": "Apache-2.0", | |||
| "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/wicg-file-system-access": "^2020.9.5", | |||
| "popmotion": "^11.0.5", | |||
| @@ -2402,9 +2402,9 @@ | |||
| "license": "MIT" | |||
| }, | |||
| "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": { | |||
| "fflate": "~0.6.10", | |||
| "meshoptimizer": "~0.18.1" | |||
| @@ -121,7 +121,7 @@ | |||
| "vitepress-plugin-nprogress": "^0.0.4" | |||
| }, | |||
| "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/wicg-file-system-access": "^2020.9.5", | |||
| "popmotion": "^11.0.5", | |||
| @@ -143,8 +143,8 @@ | |||
| "ts-browser-helpers": "^0.16.2", | |||
| "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", | |||
| "@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" | |||
| }, | |||
| "local_dependencies": { | |||
| @@ -163,6 +163,13 @@ export interface ImportAssetOptions extends ProcessRawOptions, LoadFileOptions { | |||
| * 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, | |||
| /** | |||
| * 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' | |||
| @@ -1,6 +1,6 @@ | |||
| import type {GLTF, GLTFLoaderPlugin, GLTFParser} 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 {ThreeViewer} from '../../viewer' | |||
| import {generateUUID} from '../../three' | |||
| @@ -20,6 +20,11 @@ import {ILoader} from '../IImporter' | |||
| import {ThreeSerialization} from '../../utils' | |||
| import { | |||
| DirectionalLight2, | |||
| LineGeometry2, | |||
| LineMaterial2, | |||
| LineSegmentsGeometry2, | |||
| MeshLine, | |||
| MeshLineSegments, | |||
| PerspectiveCamera0, | |||
| PhysicalMaterial, | |||
| PointLight2, | |||
| @@ -28,12 +33,15 @@ import { | |||
| UnlitMaterial, | |||
| } from '../../core' | |||
| import {AssetImporter} from '../AssetImporter' | |||
| import {ImportAddOptions} from '../AssetManager' | |||
| // 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> { | |||
| isGLTFLoader2 = true | |||
| importOptions?: ImportAddOptions | |||
| constructor(manager: LoadingManager) { | |||
| super(manager) | |||
| this.preparsers.push(glbEncryptionPreparser) | |||
| @@ -59,6 +67,15 @@ export class GLTFLoader2 extends GLTFLoader implements ILoader<GLTF, Object3D|un | |||
| 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 | |||
| */ | |||
| @@ -80,8 +97,12 @@ export class GLTFLoader2 extends GLTFLoader implements ILoader<GLTF, Object3D|un | |||
| // 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 | |||
| const useMeshLines = this.importOptions?.useMeshLines ?? GLTFLoader2.UseMeshLines | |||
| GLTFLoader.ObjectConstructors.LineBasicMaterial = useMeshLines ? LineMaterial2 as any : UnlitLineMaterial as any | |||
| return res ? super.parse(res, path, (ret)=>{ | |||
| Texture.DEFAULT_IMAGE = val | |||
| GLTFLoader.ObjectConstructors.LineBasicMaterial = useMeshLines ? LineMaterial2 as any : UnlitLineMaterial as any | |||
| onLoad && onLoad(ret) | |||
| }, onError) : onError && onError(new ErrorEvent('no data')) | |||
| }) | |||
| @@ -101,12 +122,25 @@ export class GLTFLoader2 extends GLTFLoader implements ILoader<GLTF, Object3D|un | |||
| const scene: RootSceneImportResult|undefined = res ? res.scene || !!res.scenes && res.scenes.length > 0 && res.scenes[0] : undefined as any | |||
| if (!scene) return undefined | |||
| 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 | |||
| if (!scene.userData) scene.userData = {} | |||
| if (res.userData) scene.userData.gltfExtras = res.userData // todo: put back in gltf in GLTFExporter2 | |||
| @@ -192,3 +226,49 @@ export interface GLTFPreparser{ | |||
| process(data: string | ArrayBuffer, path: string): Promise<string | ArrayBuffer> | |||
| [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) | |||
| } | |||
| } | |||
| @@ -249,6 +249,14 @@ export interface IMaterial<TE extends IMaterialEventMap = IMaterialEventMap> ext | |||
| */ | |||
| 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 | |||
| flatShading?: boolean | |||
| map?: ITexture | null | |||
| @@ -276,8 +284,11 @@ export interface IMaterial<TE extends IMaterialEventMap = IMaterialEventMap> ext | |||
| isRawShaderMaterial?: boolean | |||
| isPhysicalMaterial?: boolean | |||
| isLineMaterial?: boolean | |||
| isUnlitMaterial?: boolean | |||
| isGBufferMaterial?: boolean | |||
| isLineMaterial2?: boolean | |||
| isUnlitLineMaterial?: boolean | |||
| // [key: string]: any | |||
| } | |||
| @@ -17,3 +17,4 @@ export class BufferGeometry2<Attributes extends NormalOrGLBufferAttributes = Nor | |||
| } | |||
| } | |||
| @@ -0,0 +1,20 @@ | |||
| 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) | |||
| } | |||
| } | |||
| @@ -0,0 +1,19 @@ | |||
| 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) | |||
| } | |||
| } | |||
| @@ -0,0 +1,23 @@ | |||
| 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) | |||
| } | |||
| } | |||
| @@ -10,7 +10,12 @@ export {UnlitLineMaterial, LineBasicMaterial2} from './material/UnlitLineMateria | |||
| export {LineMaterial2} from './material/LineMaterial2' | |||
| export {LegacyPhongMaterial} from './material/LegacyPhongMaterial' | |||
| export {Mesh2} from './object/Mesh2' | |||
| export {MeshLine} from './object/MeshLine' | |||
| export {MeshLineSegments} from './object/MeshLineSegments' | |||
| 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 {DirectionalLight2} from './light/DirectionalLight2' | |||
| export {HemisphereLight2} from './light/HemisphereLight2' | |||
| @@ -41,7 +41,7 @@ export class LegacyPhongMaterial<TE extends IMaterialEventMap = IMaterialEventMa | |||
| readonly appliedMeshes: Set<IObject3D> = new Set() | |||
| readonly setDirty = iMaterialCommons.setDirty | |||
| 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)} | |||
| generator?: IMaterialGenerator | |||
| @@ -1,7 +1,26 @@ | |||
| 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 {IMaterial, IMaterialEventMap, IMaterialGenerator, IMaterialParameters, IMaterialTemplate} from '../IMaterial' | |||
| import { | |||
| IMaterial, | |||
| IMaterialEventMap, | |||
| IMaterialGenerator, | |||
| IMaterialParameters, | |||
| IMaterialTemplate, | |||
| IMaterialUserData, | |||
| } from '../IMaterial' | |||
| import {MaterialExtension} from '../../materials' | |||
| import {iMaterialCommons, threeMaterialPropList} from './iMaterialCommons' | |||
| import {IObject3D} from '../IObject' | |||
| @@ -19,12 +38,14 @@ export class LineMaterial2<TE extends IMaterialEventMap = IMaterialEventMap> ext | |||
| public static readonly TYPE = 'LineMaterial2' // not using .type because it is used by three.js | |||
| assetType = 'material' as const | |||
| declare userData: IMaterialUserData | |||
| public readonly isLineMaterial2 = true | |||
| readonly appliedMeshes: Set<IObject3D> = new Set() | |||
| readonly setDirty = iMaterialCommons.setDirty | |||
| 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)} | |||
| generator?: IMaterialGenerator | |||
| @@ -36,6 +57,8 @@ export class LineMaterial2<TE extends IMaterialEventMap = IMaterialEventMap> ext | |||
| if (customMaterialExtensions) this.registerMaterialExtensions(customMaterialExtensions) | |||
| iMaterialCommons.upgradeMaterial.call(this) | |||
| this.setValues(parameters) | |||
| // this.userData.renderToGBuffer = false | |||
| // this.userData.renderToDepth = false | |||
| } | |||
| // region Material Extension | |||
| @@ -65,7 +88,18 @@ export class LineMaterial2<TE extends IMaterialEventMap = IMaterialEventMap> ext | |||
| 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 | |||
| @@ -94,6 +128,12 @@ export class LineMaterial2<TE extends IMaterialEventMap = IMaterialEventMap> ext | |||
| expanded: true, | |||
| onChange: (ev)=>{ | |||
| 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. | |||
| this.setDirty({uiChangeEvent: ev, needsUpdate: !!ev.last, refreshUi: !!ev.last}) | |||
| }, | |||
| @@ -119,7 +159,7 @@ export class LineMaterial2<TE extends IMaterialEventMap = IMaterialEventMap> ext | |||
| */ | |||
| setValues(parameters: Material|(LineMaterialParameters&{type?:string}), allowInvalidType = true, clearCurrentUserData: boolean|undefined = undefined): 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) | |||
| return this | |||
| } | |||
| @@ -33,7 +33,7 @@ export class ObjectShaderMaterial<TE extends IMaterialEventMap = IMaterialEventM | |||
| readonly appliedMeshes: Set<IObject3D> = new Set() | |||
| readonly setDirty = iMaterialCommons.setDirty | |||
| 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)} | |||
| generator?: IMaterialGenerator | |||
| @@ -233,4 +233,4 @@ export class ObjectShaderMaterial<TE extends IMaterialEventMap = IMaterialEventM | |||
| } | |||
| } | |||
| // todo gltf material extension | |||
| // todo gltf material extension | |||
| @@ -48,7 +48,7 @@ export class PhysicalMaterial<TE extends IMaterialEventMap = IMaterialEventMap> | |||
| readonly appliedMeshes: Set<IObject3D> = new Set() | |||
| readonly setDirty = iMaterialCommons.setDirty | |||
| 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)} | |||
| generator?: IMaterialGenerator | |||
| @@ -47,7 +47,7 @@ export class ShaderMaterial2<TE extends IMaterialEventMap = IMaterialEventMap> e | |||
| readonly appliedMeshes: Set<any> = new Set() | |||
| readonly setDirty = iMaterialCommons.setDirty | |||
| 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)} | |||
| readonly isRawShaderMaterial: boolean | |||
| @@ -43,7 +43,7 @@ export class UnlitLineMaterial<TE extends IMaterialEventMap = IMaterialEventMap> | |||
| readonly appliedMeshes: Set<IObject3D> = new Set() | |||
| readonly setDirty = iMaterialCommons.setDirty | |||
| 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)} | |||
| generator?: IMaterialGenerator | |||
| @@ -44,7 +44,7 @@ export class UnlitMaterial<TE extends IMaterialEventMap = IMaterialEventMap> ext | |||
| readonly appliedMeshes: Set<IObject3D> = new Set() | |||
| readonly setDirty = iMaterialCommons.setDirty | |||
| 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)} | |||
| generator?: IMaterialGenerator | |||
| @@ -113,20 +113,25 @@ export const iMaterialCommons = { | |||
| superDispose.call(this) | |||
| }, | |||
| 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) | |||
| 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 | |||
| }, | |||
| @@ -5,11 +5,20 @@ import {Vector3} from 'three' | |||
| import {ThreeViewer} from '../../viewer' | |||
| import {UnlitMaterial} from '../material/UnlitMaterial' | |||
| import {PhysicalMaterial} from '../material/PhysicalMaterial' | |||
| import {LineMaterial2} from '../material/LineMaterial2' | |||
| import {UnlitLineMaterial} from '../material/UnlitLineMaterial' | |||
| import {IMaterial} from '../IMaterial' | |||
| // todo move somewhere? | |||
| const defaultMaterial = new UnlitMaterial() | |||
| defaultMaterial.name = 'Default Unlit Material' | |||
| 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[] { | |||
| return [ | |||
| @@ -67,6 +76,9 @@ export function makeIObject3DUiConfig(this: IObject3D, isMesh?:boolean): UiObjec | |||
| type: 'checkbox', | |||
| label: 'Visible', | |||
| property: [this, 'visible'], | |||
| onChange: (e)=>{ | |||
| this.setDirty?.({uiChangeEvent: e, refreshScene: true, refreshUi: true, change: 'visible'}) | |||
| }, | |||
| }, | |||
| { | |||
| type: 'button', | |||
| @@ -90,21 +102,27 @@ export function makeIObject3DUiConfig(this: IObject3D, isMesh?:boolean): UiObjec | |||
| type: 'input', | |||
| label: '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', | |||
| label: 'Casts Shadow', | |||
| hidden: () => !(this as any).isMesh, | |||
| hidden: () => !this.isMesh, | |||
| property: [this, 'castShadow'], | |||
| onChange: (e)=>{ | |||
| this.setDirty?.({uiChangeEvent: e, refreshScene: true, refreshUi: true, change: 'castShadow'}) | |||
| }, | |||
| }, | |||
| { | |||
| type: 'checkbox', | |||
| label: 'Receive Shadow', | |||
| hidden: () => !(this as any).isMesh, | |||
| hidden: () => !this.isMesh, | |||
| property: [this, 'receiveShadow'], | |||
| onChange: (e)=>{ | |||
| this.setDirty?.({uiChangeEvent: e, refreshScene: true, refreshUi: true, change: 'receiveShadow'}) | |||
| }, | |||
| }, | |||
| { | |||
| type: 'checkbox', | |||
| @@ -235,17 +253,39 @@ export function makeIObject3DUiConfig(this: IObject3D, isMesh?:boolean): UiObjec | |||
| { | |||
| label: 'Remove Material(s)', | |||
| 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: ()=>{ | |||
| const mat = this.materials | |||
| this.material = [defaultMaterial] | |||
| this.material = [new UnlitLineMaterial()] | |||
| return ()=> this.material = mat | |||
| }, | |||
| }, | |||
| { | |||
| label: 'New Physical Material', | |||
| 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: ()=>{ | |||
| const mat = this.materials | |||
| this.material = [new PhysicalMaterial()] | |||
| @@ -255,7 +295,7 @@ export function makeIObject3DUiConfig(this: IObject3D, isMesh?:boolean): UiObjec | |||
| { | |||
| label: 'New Unlit Material', | |||
| 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: ()=>{ | |||
| const mat = this.materials | |||
| this.material = [new UnlitMaterial()] | |||
| @@ -50,3 +50,4 @@ export class Mesh2< | |||
| // endregion | |||
| } | |||
| @@ -0,0 +1,55 @@ | |||
| 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 | |||
| } | |||
| @@ -0,0 +1,55 @@ | |||
| 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 | |||
| } | |||
| @@ -232,8 +232,14 @@ export {EffectComposer} from 'three/examples/jsm/postprocessing/EffectComposer.j | |||
| export type {GLTFLoaderPlugin, GLTF, GLTFReference, GLTFReferenceType} 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 {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 * 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' | |||
| @@ -55,6 +55,5 @@ export class OrbitControls3 extends OrbitControls implements IUiConfigContainer, | |||
| throttleUpdate = 60 // throttle to 60 updates per second (implemented in OrbitControls.js.update() method) | |||
| // todo add to three-ts-types | |||
| stopDamping!: () => void | |||
| } | |||
| @@ -1,4 +1,14 @@ | |||
| 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' | |||
| export class Box3B extends Box3 { | |||
| @@ -10,7 +20,7 @@ export class Box3B extends Box3 { | |||
| if (!object.visible && ignoreInvisible) 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), | |||
| // accounting for both the object's, and children's, world transforms | |||
| @@ -20,7 +30,7 @@ export class Box3B extends Box3 { | |||
| // InstancedMesh has boundingBox = null, so it can be computed | |||
| 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!() | |||
| @@ -42,7 +52,10 @@ export class Box3B extends Box3 { | |||
| const geometry = (object as Mesh).geometry | |||
| 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 | |||
| for (let i = 0, l = position.count; i < l; i++) { | |||
| this._vector.fromBufferAttribute(position, i).applyMatrix4(object.matrixWorld) | |||
| @@ -51,10 +64,14 @@ export class Box3B extends Box3 { | |||
| } else { | |||
| if (geometry.boundingBox === null) | |||
| 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) | |||
| } | |||
| } | |||
| } | |||
| @@ -12,9 +12,12 @@ export class BoxSelectionWidget extends SelectionWidget { | |||
| color: '#ff2222' as any, transparent: true, opacity: 0.9, | |||
| linewidth: 5, // in pixels | |||
| resolution: new Vector2(1024, 1024), // to be set by renderer, eventually | |||
| worldUnits: false, | |||
| dashed: false, | |||
| toneMapped: false, | |||
| }) | |||
| matLine.userData.renderToGBuffer = false | |||
| matLine.userData.renderToDepth = false | |||
| this.lineMaterial = matLine | |||
| const ls = new LineSegmentsGeometry() | |||
| @@ -33,7 +36,7 @@ export class BoxSelectionWidget extends SelectionWidget { | |||
| if (selected) { | |||
| const bbox = new Box3B().expandByObject(selected, false) | |||
| // 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) | |||
| } | |||
| } | |||
| @@ -43,6 +43,8 @@ export class CameraHelper2 extends ACameraHelperWidget { | |||
| depthTest: false, | |||
| depthWrite: false, | |||
| }) | |||
| material.userData.renderToGBuffer = false | |||
| material.userData.renderToDepth = false | |||
| const {vertices, colors, pointMap} = generateVertices() | |||
| @@ -43,6 +43,8 @@ export class DirectionalLightHelper2 extends ALightHelperWidget { | |||
| depthTest: false, | |||
| depthWrite: false, | |||
| }) | |||
| this.material.userData.renderToGBuffer = false | |||
| this.material.userData.renderToDepth = false | |||
| this.lightPlane = new Line2(geometry, this.material) | |||
| this.add(this.lightPlane) | |||
| @@ -41,6 +41,8 @@ export class PointLightHelper2 extends ALightHelperWidget { | |||
| depthTest: false, | |||
| depthWrite: false, | |||
| }) | |||
| this.material.userData.renderToGBuffer = false | |||
| this.material.userData.renderToDepth = false | |||
| this.lightSphere = new Wireframe(geometry, this.material) | |||
| this.lightSphere.computeLineDistances() | |||
| @@ -1,5 +1,4 @@ | |||
| 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 {LineSegmentsGeometry} from 'three/examples/jsm/lines/LineSegmentsGeometry.js' | |||
| import {onChange} from 'ts-browser-helpers' | |||
| @@ -24,7 +23,7 @@ export class SpotLightHelper2 extends ALightHelperWidget { | |||
| if (size === undefined) size = 0.5 | |||
| let geometry = new LineSegmentsGeometry() | |||
| const geometry = new LineSegmentsGeometry() | |||
| const positions = [ | |||
| 0, 0, 0, 0, 0, 1, | |||
| 0, 0, 0, 1, 0, 1, | |||
| @@ -59,13 +58,12 @@ export class SpotLightHelper2 extends ALightHelperWidget { | |||
| depthTest: false, | |||
| depthWrite: false, | |||
| }) | |||
| this.material.userData.renderToGBuffer = false | |||
| this.material.userData.renderToDepth = false | |||
| this.cone = new LineSegments2(geometry, this.material) | |||
| this.add(this.cone) | |||
| geometry = new LineGeometry() | |||
| geometry.setPositions([0, 0, 0, 0, 0, 1]) | |||
| this.update() | |||
| this.traverse(o => { | |||