@solidrt/3d 0.0.58 → 0.0.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +734 -264
- package/README.md +4 -2
- package/examples/README.md +50 -4
- package/examples/bloom.tsx +113 -0
- package/examples/cascades.tsx +4 -4
- package/examples/collision.tsx +6 -6
- package/examples/exits.tsx +96 -0
- package/examples/first-person.tsx +5 -5
- package/examples/fleet.tsx +4 -4
- package/examples/fog.tsx +5 -5
- package/examples/instanced.tsx +70 -50
- package/examples/lamps.tsx +4 -4
- package/examples/lod.tsx +125 -0
- package/examples/materials.tsx +9 -9
- package/examples/morph.tsx +174 -0
- package/examples/normals.tsx +118 -0
- package/examples/{lit.tsx → phong.tsx} +9 -9
- package/examples/polyhedra.tsx +61 -0
- package/examples/scene-atlas.tsx +70 -0
- package/examples/scene-views.tsx +5 -5
- package/examples/shadows.tsx +9 -9
- package/examples/sky-lit.tsx +3 -3
- package/examples/skybox.tsx +5 -5
- package/examples/standard.tsx +2 -2
- package/examples/streams.tsx +122 -0
- package/examples/sweep-paths.tsx +5 -5
- package/examples/wireframe.tsx +24 -2
- package/package.json +3 -3
- package/src/clip.ts +18 -5
- package/src/color.ts +11 -2
- package/src/components/context.tsx +11 -2
- package/src/components/directional-light.tsx +2 -2
- package/src/components/first-person-camera.tsx +4 -4
- package/src/components/group.tsx +9 -5
- package/src/components/hemisphere-light.tsx +2 -2
- package/src/components/index.ts +4 -1
- package/src/components/instanced-mesh.tsx +28 -10
- package/src/components/lod.tsx +102 -0
- package/src/components/mesh.tsx +24 -6
- package/src/components/node-props.ts +35 -5
- package/src/components/orbit-camera.tsx +15 -12
- package/src/components/point-light.tsx +2 -2
- package/src/components/record-mesh.tsx +12 -7
- package/src/components/scene.tsx +55 -2
- package/src/components/spot-light.tsx +2 -2
- package/src/components/sprite.tsx +2 -2
- package/src/components/view3d.tsx +18 -1
- package/src/environment.ts +9 -8
- package/src/first-person.ts +125 -38
- package/src/geometry-gpu.ts +172 -39
- package/src/geometry.ts +1573 -239
- package/src/glsl.ts +398 -113
- package/src/gltf.ts +363 -141
- package/src/index.ts +31 -21
- package/src/light.ts +7 -8
- package/src/lod.ts +148 -0
- package/src/material.ts +316 -202
- package/src/mesh.ts +490 -256
- package/src/mixer.ts +27 -14
- package/src/model-file.ts +54 -15
- package/src/model.ts +86 -21
- package/src/motion.ts +17 -0
- package/src/node.ts +421 -22
- package/src/orbit.ts +378 -116
- package/src/profile.ts +4 -25
- package/src/resolve.ts +181 -0
- package/src/scene-shadows.ts +94 -93
- package/src/scene.ts +605 -185
- package/src/sweep.ts +27 -8
- package/tools/model.ts +3 -1
package/src/index.ts
CHANGED
|
@@ -5,11 +5,13 @@
|
|
|
5
5
|
// without Solid components) and the component face (Scene/Mesh/Group/
|
|
6
6
|
// PerspectiveCamera) on top. See AGENTS.md for the model and the traps.
|
|
7
7
|
|
|
8
|
-
export { add, createGroup, getRotation, getTransform, lookAt, remove, setTransform, setTransition, setVisible, worldPosition } from "./node.ts"
|
|
9
|
-
export type { NodePointerEvent, NodeTapEvent, NodeWheelEvent, SceneEventBase, SceneNode, ScenePointerEvent, ScenePointerListener, SceneTapEvent, SceneWheelEvent, TransformUpdate, TransitionEndEvent } from "./node.ts"
|
|
10
|
-
export { addInstance, createInstancedMesh, createMesh, createRecordMesh, createSprite, disposeInstances,
|
|
11
|
-
export type { InstancedMesh as InstancedMeshNode, InstancedMeshOptions, InstanceNode, InstanceSlots,
|
|
12
|
-
export { createDirectionalLight, createHemisphereLight, createPointLight, createSpotLight, setLight
|
|
8
|
+
export { add, createGroup, destroy, getMorphNames, getMorphWeights, getRotation, getTransform, lookAt, remove, setMorphWeights, setTransform, setTransition, setVisible, worldPosition } from "./node.ts"
|
|
9
|
+
export type { LodConfig, MorphWeights, NodePointerEvent, NodeTapEvent, NodeWheelEvent, SceneEventBase, SceneNode, ScenePointerEvent, ScenePointerListener, SceneTapEvent, SceneWheelEvent, TransformUpdate, TransitionEndEvent } from "./node.ts"
|
|
10
|
+
export { addInstance, createInstancedMesh, createMesh, createRecordMesh, createSprite, disposeInstances, setCastShadow, setCulling, setDrawRange, setGeometry, setInstanceStyle, setLayers, setMaterial, setMeshParams, setRecordCount, setRecords, setRenderOrder, instanceAttribute, updateRecords, INSTANCE_FLOATS } from "./mesh.ts"
|
|
11
|
+
export type { InstancedMesh as InstancedMeshNode, InstancedMeshOptions, InstanceNode, InstanceSlots, InstanceStream, Mesh as MeshNode, MeshInstances, RecordMesh as RecordMeshNode, RecordMeshOptions, UpdateRecordsOptions } from "./mesh.ts"
|
|
12
|
+
export { createDirectionalLight, createHemisphereLight, createPointLight, createSpotLight, setLight } from "./light.ts"
|
|
13
|
+
// The shader-source caps, also on the /glsl subpath next to the sources they size.
|
|
14
|
+
export { MAX_CASCADES, MAX_LIGHTS, MAX_SHADOW_MAPS } from "./glsl.ts"
|
|
13
15
|
export type {
|
|
14
16
|
DirectionalLight as DirectionalLightNode,
|
|
15
17
|
DirectionalLightOptions,
|
|
@@ -25,36 +27,44 @@ export type {
|
|
|
25
27
|
SpotLightOptions,
|
|
26
28
|
SpotShadowOptions,
|
|
27
29
|
} from "./light.ts"
|
|
28
|
-
export {
|
|
30
|
+
export { createInstancedLod, createLod, setLod } from "./lod.ts"
|
|
31
|
+
export type { InstancedLodLevel, InstancedLodOptions, LodLevel, LodOptions } from "./lod.ts"
|
|
32
|
+
export { createScene, resolveParams } from "./scene.ts"
|
|
29
33
|
export { feedPointer } from "./scene-pointer.ts"
|
|
30
|
-
export type { Capsule, EnvironmentOptions, FogOptions, Hit, Impact, MoveOptions, MoveResult, OrientedBox, Overlap, QueryOptions, ReflectionProbe, ReflectionProbeOptions, Scene as SceneHandle, SceneHandlers, SceneOptions, ScreenRay, SkyboxOptions, Sphere, ToneMapping, ViewHandle, ViewOptions, Volume } from "./scene.ts"
|
|
34
|
+
export type { BloomOptions, Capsule, EnvironmentOptions, FogOptions, Hit, Impact, MoveOptions, MoveResult, OrientedBox, Overlap, QueryOptions, ReflectionProbe, ReflectionProbeOptions, ResolveInput, ResolveOptions, Scene as SceneHandle, SceneHandlers, SceneOptions, ScreenRay, SkyboxOptions, Sphere, ToneMapping, ViewHandle, ViewOptions, Volume } from "./scene.ts"
|
|
31
35
|
export { moveAndSlide } from "./collision.ts"
|
|
32
36
|
export type { MoveScene } from "./collision.ts"
|
|
33
|
-
export { equirectToCube, loadEnvironment } from "./environment.ts"
|
|
34
|
-
export {
|
|
37
|
+
export { bufferFormat, equirectToCube, loadEnvironment } from "./environment.ts"
|
|
38
|
+
export type { BufferFormat } from "./environment.ts"
|
|
39
|
+
export { linearColor, linearToSrgb, premultipliedColor, srgbToLinear } from "./color.ts"
|
|
35
40
|
export type { CameraState, CameraUpdate, OrthoExtent } from "./camera.ts"
|
|
36
|
-
export type { NodeTransition, NodeTransitionSpec } from "flux:spatial"
|
|
37
|
-
export { disposeGeometry } from "./geometry-gpu.ts"
|
|
38
|
-
export {
|
|
39
|
-
export
|
|
40
|
-
export {
|
|
41
|
+
export type { NodeMotionSpec, NodeTransition, NodeTransitionSpec } from "flux:spatial"
|
|
42
|
+
export { computeVertexNormals, disposeGeometry, updateVertices } from "./geometry-gpu.ts"
|
|
43
|
+
export type { GeometryBuffers, UpdateVerticesOptions } from "./geometry-gpu.ts"
|
|
44
|
+
export { arrowHelper, axesHelper, box, box3Helper, capsule, capsuleHelper, circle, cone, cylinder, dodecahedron, fillAttribute, fillColors, edgesGeometry, mergeVertices, normalsHelper, toNonIndexed, withNormals, geometryBounds, geometryTopology, gridHelper, icosahedron, attributeAccess, geometryAttribute, geometryKey, geometryLayouts, geometrySlot, geometryStreams, geometryVertexCount, isFloatFormat, isFloatLayout, layoutAttributes, layoutKey, layoutSlot, layoutStride, mergeGeometries, octahedron, packGeometry, packMorphTargets, plane, planeHelper, polyhedron, ring, sphere, tetrahedron, torus, torusKnot, transformGeometry, validateGeometry, wireframeGeometry, vertexBytes, vertexCount, vertexView, withAttribute, withColors, withMorphTargets, MORPH_ENTRY_TEXELS, MORPH_TEXEL_FLOATS, BASE_FLOATS, VERTEX_FORMATS, VERTEX_LAYOUTS } from "./geometry.ts"
|
|
45
|
+
export type { ArrowHelperOptions, AttributeAccess, AttributeFill, VertexStream, WithAttributeOptions, AxesHelperOptions, BoxOptions, CapsuleHelperOptions, CapsuleOptions, CircleOptions, ColorFill, ConeOptions, CylinderOptions, FormatCodec, Geometry, GeometryOptions, GridHelperOptions, MorphTarget, MorphTargets, NormalsHelperOptions, PlaneHelperOptions, PlaneOptions, PolyhedronOptions, RingOptions, SphereOptions, TorusKnotOptions, TorusOptions, VertexLayout } from "./geometry.ts"
|
|
46
|
+
export { fillet, roundRect, triangulate } from "./profile.ts"
|
|
41
47
|
export type { Profile, ProfilePoint } from "./profile.ts"
|
|
42
|
-
export { extrude, lathe, pathFrames, sweep, tube } from "./sweep.ts"
|
|
48
|
+
export { extrude, lathe, pathFrames, polygon, sweep, tube } from "./sweep.ts"
|
|
43
49
|
export type { ExtrudeOptions, LatheOptions, PathFrames, PathPoint, SweepPath, TubeOptions } from "./sweep.ts"
|
|
44
|
-
export {
|
|
45
|
-
export type { LitOptions, Material, ShaderMaterialClass, ShaderMaterialClassOptions, ShaderMaterialInstanceOptions, ShaderMaterialOptions, SpriteOptions, StandardOptions, UnlitOptions } from "./material.ts"
|
|
46
|
-
export { DirectionalLight, FirstPersonCamera, Group, HemisphereLight, Instance, InstancedMesh, Mesh, OrbitCamera, PerspectiveCamera, PointLight, RecordMesh, Scene, SpotLight, Sprite, View3d, useScene } from "./components/index.ts"
|
|
50
|
+
export { phong, shaderMaterial, shaderMaterialClass, sprite, standard, unlit } from "./material.ts"
|
|
51
|
+
export type { LitOptions, Material, PhongOptions, ShaderMaterialClass, ShaderMaterialClassOptions, ShaderMaterialInstanceOptions, ShaderMaterialOptions, SpriteOptions, StandardOptions, UnlitOptions } from "./material.ts"
|
|
52
|
+
export { DirectionalLight, FirstPersonCamera, Group, HemisphereLight, Instance, InstancedLod, InstancedMesh, Lod, Mesh, OrbitCamera, PerspectiveCamera, PointLight, RecordMesh, Scene, SpotLight, Sprite, View3d, useScene } from "./components/index.ts"
|
|
47
53
|
export type {
|
|
48
54
|
CameraTarget,
|
|
49
55
|
DirectionalLightProps,
|
|
50
56
|
FirstPersonCameraProps,
|
|
51
57
|
HemisphereLightProps,
|
|
58
|
+
InstancedLodProps,
|
|
52
59
|
InstancedMeshProps,
|
|
53
60
|
InstanceProps,
|
|
61
|
+
LodProps,
|
|
54
62
|
MeshProps,
|
|
55
63
|
OrbitCameraProps,
|
|
56
64
|
PerspectiveCameraProps,
|
|
57
65
|
PointerEventProps,
|
|
66
|
+
BubblingPointerEventProps,
|
|
67
|
+
GroupProps,
|
|
58
68
|
PointLightProps,
|
|
59
69
|
RecordMeshProps,
|
|
60
70
|
ScenePointerProps,
|
|
@@ -72,11 +82,11 @@ export type { Model, ModelMaps, ModelOptions } from "./model.ts"
|
|
|
72
82
|
export { bindSkeleton } from "./skeleton.ts"
|
|
73
83
|
export { createMixer } from "./mixer.ts"
|
|
74
84
|
export type { Mixer, MixerOptions, MixerPlayOptions } from "./mixer.ts"
|
|
75
|
-
export { sampleChannel } from "./clip.ts"
|
|
85
|
+
export { channelElements, sampleChannel } from "./clip.ts"
|
|
76
86
|
export { createOrbitCamera } from "./orbit.ts"
|
|
77
|
-
export type { OrbitAxes, OrbitCamera as OrbitCameraHandle, OrbitCameraOptions, OrbitPose, OrbitTarget } from "./orbit.ts"
|
|
87
|
+
export type { OrbitAxes, OrbitCamera as OrbitCameraHandle, OrbitCameraOptions, OrbitPose, OrbitPoseState, OrbitTarget } from "./orbit.ts"
|
|
78
88
|
export { createFirstPersonCamera } from "./first-person.ts"
|
|
79
|
-
export type { FirstPersonAxes, FirstPersonCamera as FirstPersonCameraHandle, FirstPersonCameraOptions, FirstPersonPose, FirstPersonTarget } from "./first-person.ts"
|
|
89
|
+
export type { FirstPersonAxes, FirstPersonCamera as FirstPersonCameraHandle, FirstPersonCameraOptions, FirstPersonPose, FirstPersonPoseState, FirstPersonTarget } from "./first-person.ts"
|
|
80
90
|
export { firstPersonActions, firstPersonBindings, orbitActions, orbitBindings } from "./input.ts"
|
|
81
91
|
export type { CameraDevices } from "./input.ts"
|
|
82
92
|
// math's lookAt (the camera view matrix) stays on the /math subpath: the
|
package/src/light.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// hemisphere ambient, their constructors and the setLight write path.
|
|
3
3
|
// Scene reactions go through the node's SceneHooks (node.ts).
|
|
4
4
|
|
|
5
|
-
import { MAX_CASCADES
|
|
5
|
+
import { MAX_CASCADES } from "./glsl.ts"
|
|
6
6
|
import type { Vec3 } from "./math.ts"
|
|
7
7
|
import { makeNode } from "./node.ts"
|
|
8
8
|
import type { SceneNode } from "./node.ts"
|
|
@@ -204,13 +204,12 @@ export type PointLightOptions = {
|
|
|
204
204
|
}
|
|
205
205
|
export type HemisphereLightOptions = { sky?: Vec3; ground?: Vec3; intensity?: number }
|
|
206
206
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
export { MAX_CASCADES }
|
|
207
|
+
// Every light may cast; the bound is the shadow-slot budget
|
|
208
|
+
// (MAX_SHADOW_MAPS in glsl.ts, exported from the root with MAX_LIGHTS and
|
|
209
|
+
// MAX_CASCADES: a directional light claims `shadow.cascades` slots, a
|
|
210
|
+
// point light six, a spot one). Every map is a tile of the scene's one
|
|
211
|
+
// shadow atlas, so the pass count never follows the caster count, the
|
|
212
|
+
// fill does.
|
|
214
213
|
|
|
215
214
|
// The spot cone bounds: a half-angle in (0, 90] degrees, penumbra a
|
|
216
215
|
// 0..1 fraction of it.
|
package/src/lod.ts
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// Level of detail: a group whose children are variants of one object,
|
|
2
|
+
// nearest first, with the projected size below which each hands over to
|
|
3
|
+
// the next; and the instanced form, a population whose every instance
|
|
4
|
+
// picks its own level. The choice is the spatial core's, per target,
|
|
5
|
+
// after each flush (okf/done/3d-lod.md): a thousand groups cost no JS
|
|
6
|
+
// per frame. Three's LOD vocabulary over Unity's screen-relative model.
|
|
7
|
+
import { add, createGroup } from "./node.ts"
|
|
8
|
+
import type { LodConfig, SceneNode } from "./node.ts"
|
|
9
|
+
import { createInstancedMesh, setCastShadow } from "./mesh.ts"
|
|
10
|
+
import type { InstancedMesh, InstancedMeshOptions } from "./mesh.ts"
|
|
11
|
+
import type { Geometry } from "./geometry.ts"
|
|
12
|
+
import type { Material } from "./material.ts"
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* One level of a LOD group: the node drawn at this level and `size`, the
|
|
16
|
+
* group's projected size - the diameter of the sphere around its levels'
|
|
17
|
+
* boxes (radius = half the box diagonal) as a fraction of the viewport
|
|
18
|
+
* height - BELOW which the level hands over to the next (Unity's screen
|
|
19
|
+
* relative transition height). The last level's size is the cull
|
|
20
|
+
* threshold: 0 keeps it drawn at any distance. For a perspective camera
|
|
21
|
+
* `size = radius / (distance * tan(fov / 2))`, so a tree whose box is 4
|
|
22
|
+
* across (radius 3.5) at 30 units under a 60 degree camera measures about
|
|
23
|
+
* 0.2.
|
|
24
|
+
*/
|
|
25
|
+
export type LodLevel = { node: SceneNode; size: number }
|
|
26
|
+
|
|
27
|
+
export type LodOptions = {
|
|
28
|
+
/** The cross-fade band as a fraction of each threshold (default 0, a
|
|
29
|
+
* hard switch with hysteresis): a boundary at size `s` widens to
|
|
30
|
+
* `[s, s * (1 + fade))`, in which both levels draw with complementary
|
|
31
|
+
* screen-hash dithers (the stock materials; a custom class composes
|
|
32
|
+
* LOD_FADE). Shadow tiles never fade: their depth pass switches hard
|
|
33
|
+
* at the band's midpoint. */
|
|
34
|
+
fade?: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// A level's size and the band fraction, checked where the mistake is
|
|
38
|
+
// made; the core re-checks with the same rules.
|
|
39
|
+
function checkSizes(levels: { size: number }[], fade: number | undefined, site: string): number {
|
|
40
|
+
levels.forEach((l, k) => {
|
|
41
|
+
if (!(Number.isFinite(l.size) && l.size >= 0)) throw new Error(site + ": level " + k + " size must be a finite number >= 0, got " + l.size)
|
|
42
|
+
if (k > 0 && l.size >= levels[k - 1]!.size) throw new Error(site + ": level sizes must be strictly descending, nearest first (level " + k + ": " + l.size + " after " + levels[k - 1]!.size + ")")
|
|
43
|
+
})
|
|
44
|
+
let f = fade ?? 0
|
|
45
|
+
if (!(Number.isFinite(f) && f >= 0)) throw new Error(site + ": fade must be a finite fraction >= 0, got " + f)
|
|
46
|
+
return f
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A LOD group (Three's LOD, Unity's LODGroup): a group node whose levels
|
|
51
|
+
* are its children, one drawn at a time by projected size on every
|
|
52
|
+
* target - the scene and each view by their own camera, shadow tiles by
|
|
53
|
+
* the scene camera, so a caster's shadow matches the level the camera
|
|
54
|
+
* sees. The choice is the spatial core's after each flush; zero JS per
|
|
55
|
+
* frame however many groups. Picking and collision through the scene see
|
|
56
|
+
* the level the scene draws (a view's pick the view's). The group is
|
|
57
|
+
* measured by the sphere around its levels' boxes; nested groups chain.
|
|
58
|
+
* Re-declare with setLod; `setLod(group, [])` makes it a plain group.
|
|
59
|
+
*/
|
|
60
|
+
export function createLod(levels: LodLevel[], opts?: LodOptions): SceneNode {
|
|
61
|
+
let group = createGroup()
|
|
62
|
+
setLod(group, levels, opts)
|
|
63
|
+
return group
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Declare (or with an empty list clear) a node's LOD levels. A level is
|
|
68
|
+
* a direct child of the node: a parentless node is adopted as one here,
|
|
69
|
+
* a node parented elsewhere is refused (move it with `add` first - a
|
|
70
|
+
* declaration never re-parents behind your back). On the population of
|
|
71
|
+
* createInstancedLod the levels are sizes only (`{ size }`): its level
|
|
72
|
+
* meshes are fixed at creation.
|
|
73
|
+
*/
|
|
74
|
+
export function setLod(node: SceneNode, levels: (LodLevel | { size: number })[], opts?: LodOptions): void {
|
|
75
|
+
if (node._destroyed) throw new Error("setLod: the node was destroyed")
|
|
76
|
+
let fade = checkSizes(levels, opts?.fade, "setLod")
|
|
77
|
+
let population = node.kind === "mesh" && (node as InstancedMesh)._instances?.levels !== null && (node as InstancedMesh)._instances !== null
|
|
78
|
+
let config: LodConfig | null = null
|
|
79
|
+
if (levels.length > 0) {
|
|
80
|
+
let nodes = levels.map(l => ("node" in l ? l.node : null))
|
|
81
|
+
if (population) {
|
|
82
|
+
if (nodes.some(n => n !== null)) throw new Error("setLod: an instanced LOD's levels are its meshes, fixed at creation - pass sizes only")
|
|
83
|
+
let count = (node as InstancedMesh)._instances.levels!.length
|
|
84
|
+
if (levels.length !== count) throw new Error("setLod: an instanced LOD with " + count + " level meshes takes " + count + " sizes, got " + levels.length)
|
|
85
|
+
} else {
|
|
86
|
+
if (nodes.some(n => n === null)) throw new Error("setLod: every level names a node")
|
|
87
|
+
for (let n of nodes as SceneNode[]) {
|
|
88
|
+
if (n === node) throw new Error("setLod: a level cannot be the group itself")
|
|
89
|
+
if (n.kind === "instance") throw new Error("setLod: an instance is slot-bound to its mesh and cannot be a level")
|
|
90
|
+
if (n._destroyed) throw new Error("setLod: a level node was destroyed")
|
|
91
|
+
if (nodes.indexOf(n) !== nodes.lastIndexOf(n)) throw new Error("setLod: a node is listed twice")
|
|
92
|
+
if (n.parent !== null && n.parent !== node) throw new Error("setLod: a level must be a direct child of the group (or parentless, to be adopted); add it first")
|
|
93
|
+
}
|
|
94
|
+
for (let n of nodes as SceneNode[]) if (n.parent !== node) add(node, n)
|
|
95
|
+
}
|
|
96
|
+
config = { levels: levels.map((l, k) => ({ node: nodes[k]!, size: l.size })), fade }
|
|
97
|
+
}
|
|
98
|
+
node._lod = config
|
|
99
|
+
if (node._scene !== null) node._scene._bindLod(node)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** One level of an instanced LOD: what the population draws at this
|
|
103
|
+
* level, and the projected size (per INSTANCE) below which it hands
|
|
104
|
+
* over - see LodLevel. */
|
|
105
|
+
export type InstancedLodLevel = { geometry: Geometry; material: Material; size: number }
|
|
106
|
+
|
|
107
|
+
export type InstancedLodOptions = InstancedMeshOptions &
|
|
108
|
+
LodOptions & {
|
|
109
|
+
/** Every level draws into the scene's shadow map (setCastShadow on
|
|
110
|
+
* each); default false. */
|
|
111
|
+
castShadow?: boolean
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* An instanced LOD: one population whose every instance draws at the
|
|
116
|
+
* level its OWN projected size picks - a forest as three draw entries
|
|
117
|
+
* (mesh, simplified mesh, card) whatever the spread, the near trees full
|
|
118
|
+
* and the far ones cards. Returns the first level's mesh, which is the
|
|
119
|
+
* population: addInstance / `<Instance>` on it, its instance nodes place
|
|
120
|
+
* and pick as on any InstancedMesh, and the other levels are its
|
|
121
|
+
* children at identity, each with a matrix buffer of its own the core
|
|
122
|
+
* stages an instance's record into (`_instances.levels`, nearest first;
|
|
123
|
+
* their per-level style streams take setInstanceStyle together). A pick
|
|
124
|
+
* or pointer event on an instance names that first mesh whatever level
|
|
125
|
+
* the instance is drawn at (the instance is the population's). Records
|
|
126
|
+
* are target-agnostic, so instances pick by the SCENE camera and every
|
|
127
|
+
* view and shadow tile draws that choice; instanced levels switch hard
|
|
128
|
+
* with hysteresis (`fade` is refused). Layers and castShadow set on the
|
|
129
|
+
* population reach every level.
|
|
130
|
+
*/
|
|
131
|
+
export function createInstancedLod(levels: InstancedLodLevel[], opts?: InstancedLodOptions): InstancedMesh {
|
|
132
|
+
if (levels.length === 0) throw new Error("createInstancedLod: at least one level")
|
|
133
|
+
let fade = checkSizes(levels, opts?.fade, "createInstancedLod")
|
|
134
|
+
if (fade !== 0) throw new Error("createInstancedLod: instanced levels switch hard (a per-instance fade would need a per-record value); drop fade")
|
|
135
|
+
let meshes = levels.map((l, k) =>
|
|
136
|
+
createInstancedMesh(l.geometry, l.material, { capacity: opts?.capacity, bounds: opts?.bounds, label: opts?.label !== undefined ? opts.label + "-lod" + k : undefined }),
|
|
137
|
+
)
|
|
138
|
+
let population = meshes[0]!
|
|
139
|
+
let shared = population._instances.nodes
|
|
140
|
+
for (let m of meshes) {
|
|
141
|
+
m._instances.nodes = shared
|
|
142
|
+
m._instances.levels = meshes
|
|
143
|
+
}
|
|
144
|
+
for (let m of meshes.slice(1)) add(population, m)
|
|
145
|
+
population._lod = { levels: levels.map(l => ({ node: null, size: l.size })), fade }
|
|
146
|
+
if (opts?.castShadow === true) setCastShadow(population, true)
|
|
147
|
+
return population
|
|
148
|
+
}
|