| GLTFObject3DExtrasExtension, | GLTFObject3DExtrasExtension, | ||||
| GLTFViewerConfigExtension, | GLTFViewerConfigExtension, | ||||
| } from '../gltf' | } from '../gltf' | ||||
| import {GLTFMeshGpuInstancingExporter} from '../../three/utils/gpu-instancing' | |||||
| export interface GLTFExporter2Options { | export interface GLTFExporter2Options { | ||||
| /** | /** | ||||
| GLTFMaterialsDisplacementMapExtension.Export, | GLTFMaterialsDisplacementMapExtension.Export, | ||||
| GLTFMaterialsLightMapExtension.Export, | GLTFMaterialsLightMapExtension.Export, | ||||
| GLTFMaterialsAlphaMapExtension.Export, | GLTFMaterialsAlphaMapExtension.Export, | ||||
| (w)=>new GLTFMeshGpuInstancingExporter(w), | |||||
| ] | ] | ||||
| setup(viewer: ThreeViewer, extraExtensions?: ((parser: GLTFWriter2) => GLTFExporterPlugin)[]): this { | setup(viewer: ThreeViewer, extraExtensions?: ((parser: GLTFWriter2) => GLTFExporterPlugin)[]): this { |
| import { | import { | ||||
| BufferGeometry, | |||||
| Color, | Color, | ||||
| EquirectangularReflectionMapping, | EquirectangularReflectionMapping, | ||||
| EventListener, | EventListener, | ||||
| } from 'three' | } from 'three' | ||||
| import type {IObject3D, IObjectProcessor} from '../IObject' | import type {IObject3D, IObjectProcessor} from '../IObject' | ||||
| import {type ICamera} from '../ICamera' | import {type ICamera} from '../ICamera' | ||||
| import {bindToValue, Box3B} from '../../three' | |||||
| import {autoGPUInstanceMeshes, bindToValue, Box3B} from '../../three' | |||||
| import {AnyOptions, onChange2, onChange3, serialize} from 'ts-browser-helpers' | import {AnyOptions, onChange2, onChange3, serialize} from 'ts-browser-helpers' | ||||
| import {PerspectiveCamera2} from '../camera/PerspectiveCamera2' | import {PerspectiveCamera2} from '../camera/PerspectiveCamera2' | ||||
| import {ThreeSerialization} from '../../utils' | import {ThreeSerialization} from '../../utils' | ||||
| this.setDirty({refreshScene: true}) | this.setDirty({refreshScene: true}) | ||||
| } | } | ||||
| @uiButton(undefined, {sendArgs: false}) | |||||
| @uiButton('Center All Geometries', {sendArgs: false}) | |||||
| centerAllGeometries(keepPosition = true, obj?: IObject3D) { | centerAllGeometries(keepPosition = true, obj?: IObject3D) { | ||||
| const geoms = new Set<IGeometry>() | const geoms = new Set<IGeometry>() | ||||
| ;(obj ?? this.modelRoot).traverse((o) => o.geometry && geoms.add(o.geometry)) | ;(obj ?? this.modelRoot).traverse((o) => o.geometry && geoms.add(o.geometry)) | ||||
| }) | }) | ||||
| } | } | ||||
| @uiButton('Auto GPU Instance Meshes') | |||||
| autoGPUInstanceMeshes() { | |||||
| const geoms = new Set<BufferGeometry>() | |||||
| this.modelRoot.traverse((o) => o.geometry && geoms.add(o.geometry)) | |||||
| geoms.forEach((g: any) => autoGPUInstanceMeshes(g)) | |||||
| } | |||||
| private _v1 = new Vector3() | private _v1 = new Vector3() | ||||
| private _v2 = new Vector3() | private _v2 = new Vector3() | ||||
| NormalBlending, | NormalBlending, | ||||
| NoToneMapping, | NoToneMapping, | ||||
| PCFShadowMap, | PCFShadowMap, | ||||
| ShadowMapType, | |||||
| Texture, | Texture, | ||||
| Vector2, | Vector2, | ||||
| Vector4, | Vector4, | ||||
| WebGLMultipleRenderTargets, | WebGLMultipleRenderTargets, | ||||
| WebGLRenderer, | WebGLRenderer, | ||||
| WebGLRenderTarget, | WebGLRenderTarget, | ||||
| WebGLRenderTargetOptions, | |||||
| WebGLRenderTargetOptions, WebGLShadowMap, | |||||
| } from 'three' | } from 'three' | ||||
| import {EffectComposer2, IPassID, IPipelinePass, sortPasses} from '../postprocessing' | import {EffectComposer2, IPassID, IPipelinePass, sortPasses} from '../postprocessing' | ||||
| import {IRenderTarget} from './RenderTarget' | import {IRenderTarget} from './RenderTarget' | ||||
| serialize, | serialize, | ||||
| ValOrArr, | ValOrArr, | ||||
| } from 'ts-browser-helpers' | } from 'ts-browser-helpers' | ||||
| import {uiButton, uiConfig, uiFolderContainer, uiMonitor, uiSlider, uiToggle} from 'uiconfig.js' | |||||
| import {generateUUID, textureDataToImageData} from '../three' | |||||
| import {uiButton, uiConfig, uiDropdown, uiFolderContainer, uiMonitor, uiSlider, uiToggle} from 'uiconfig.js' | |||||
| import {bindToValue, generateUUID, textureDataToImageData} from '../three' | |||||
| import {BlobExt, EXRExporter2} from '../assetmanager' | import {BlobExt, EXRExporter2} from '../assetmanager' | ||||
| import {RendererBlitOptions} from '../core/IRenderer' | import {RendererBlitOptions} from '../core/IRenderer' | ||||
| } | } | ||||
| } | } | ||||
| @serialize() | |||||
| @uiDropdown('Shadow Map Type', ['BasicShadowMap', 'PCFShadowMap', 'PCFSoftShadowMap', 'VSMShadowMap'].map((v, i) => ({label: v, value: i}))) | |||||
| @bindToValue({obj: 'shadowMap', key: 'type', onChange: RenderManager.prototype._shadowMapTypeChanged}) | |||||
| shadowMapType: ShadowMapType | |||||
| @bindToValue({obj: 'renderer', key: 'shadowMap'}) | |||||
| shadowMap: WebGLShadowMap | |||||
| private _shadowMapTypeChanged() { | |||||
| this.resetShadows() | |||||
| this.reset() | |||||
| } | |||||
| @uiConfig(undefined, {label: 'Passes'}) | @uiConfig(undefined, {label: 'Passes'}) | ||||
| private _passes: IPipelinePass[] = [] | private _passes: IPipelinePass[] = [] | ||||
| private _pipeline: IPassID[] = [] | private _pipeline: IPassID[] = [] |
| import {IGeometry, IMaterial, IObject3D} from '../../core' | import {IGeometry, IMaterial, IObject3D} from '../../core' | ||||
| import {BufferAttribute, InstancedMesh} from 'three' | |||||
| import {BufferAttribute, InstancedMesh, Matrix4, Quaternion, Vector3} from 'three' | |||||
| // noinspection ES6PreferShortImport | // noinspection ES6PreferShortImport | ||||
| import {copyObject3DUserData} from '../../utils/serialization' | import {copyObject3DUserData} from '../../utils/serialization' | ||||
| import {GLTFWriter2} from '../../assetmanager' | |||||
| import {GLTFExporterPlugin} from 'three/examples/jsm/exporters/GLTFExporter' | |||||
| export function autoGPUInstanceMeshes(matOrGeom: IMaterial|IGeometry) { | export function autoGPUInstanceMeshes(matOrGeom: IMaterial|IGeometry) { | ||||
| if (!(<IMaterial>matOrGeom).isMaterial && !(<IGeometry>matOrGeom).isBufferGeometry) return | if (!(<IMaterial>matOrGeom).isMaterial && !(<IGeometry>matOrGeom).isBufferGeometry) return | ||||
| ;(parent as any).setDirty() | ;(parent as any).setDirty() | ||||
| } | } | ||||
| } | } | ||||
| export class GLTFMeshGpuInstancingExporter implements GLTFExporterPlugin { | |||||
| name = 'EXT_mesh_gpu_instancing' | |||||
| constructor(public writer: GLTFWriter2) { | |||||
| } | |||||
| writeNode(object: any, nodeDef: any): void { | |||||
| if (!object.isInstancedMesh) return | |||||
| const writer = this.writer | |||||
| const mesh = object as InstancedMesh | |||||
| // @ts-expect-error not in ts | |||||
| let attributes: any = mesh.sourceTrs | |||||
| if (!attributes) { | |||||
| const translationAttr = new Float32Array(mesh.count * 3) | |||||
| const rotationAttr = new Float32Array(mesh.count * 4) | |||||
| const scaleAttr = new Float32Array(mesh.count * 3) | |||||
| const matrix = new Matrix4() | |||||
| const position = new Vector3() | |||||
| const quaternion = new Quaternion() | |||||
| const scale = new Vector3() | |||||
| for (let i = 0; i < mesh.count; i++) { | |||||
| mesh.getMatrixAt(i, matrix) | |||||
| matrix.decompose(position, quaternion, scale) | |||||
| position.toArray(translationAttr, i * 3) | |||||
| quaternion.toArray(rotationAttr, i * 4) | |||||
| scale.toArray(scaleAttr, i * 3) | |||||
| } | |||||
| attributes = { | |||||
| TRANSLATION: new BufferAttribute(translationAttr, 3), | |||||
| ROTATION: new BufferAttribute(rotationAttr, 4), | |||||
| SCALE: new BufferAttribute(scaleAttr, 3), | |||||
| } | |||||
| } | |||||
| attributes = { | |||||
| // @ts-expect-error todo add to ts | |||||
| TRANSLATION: writer.processAccessor(attributes.TRANSLATION), | |||||
| ROTATION: (writer as any).processAccessor(attributes.ROTATION), | |||||
| SCALE: (writer as any).processAccessor(attributes.SCALE), | |||||
| } | |||||
| if (mesh.instanceColor) | |||||
| attributes._COLOR_0 = (writer as any).processAccessor(mesh.instanceColor) | |||||
| writer.extensionsUsed[ this.name ] = true | |||||
| // @ts-expect-error todo add to ts | |||||
| writer.extensionsRequired[ this.name ] = true | |||||
| nodeDef.extensions = nodeDef.extensions || {} | |||||
| nodeDef.extensions[ this.name ] = {attributes} | |||||
| } | |||||
| } |
| export type {Serializer} from 'ts-browser-helpers' | export type {Serializer} from 'ts-browser-helpers' | ||||
| export {PointerDragHelper} from 'ts-browser-helpers' | export {PointerDragHelper} from 'ts-browser-helpers' | ||||
| export {JSUndoManager} from 'ts-browser-helpers' | |||||
| export type {JSUndoManagerCommand2, JSUndoManagerCommand, JSUndoManagerOptions, JSUndoManagerCommand1} from 'ts-browser-helpers' | |||||
| export {Damper} from 'ts-browser-helpers' | export {Damper} from 'ts-browser-helpers' | ||||
| export {SimpleEventDispatcher} from 'ts-browser-helpers' | export {SimpleEventDispatcher} from 'ts-browser-helpers' | ||||