model-preview 0.1.0

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.
@@ -0,0 +1,131 @@
1
+ import {
2
+ BufferGeometry,
3
+ Color,
4
+ Float32BufferAttribute,
5
+ Group,
6
+ Line,
7
+ ShaderMaterial,
8
+ Vector3,
9
+ type Line as ThreeLine,
10
+ } from 'three';
11
+
12
+ import { AXIS_X_COLOR, AXIS_Y_COLOR, AXIS_Z_COLOR, HELPER_OBJECT_TYPE } from '../constants.js';
13
+ import { getViewAspect, getViewHeightAtDistance, type ViewCamera } from './ViewportUtils.js';
14
+
15
+ const _origin = new Vector3();
16
+
17
+ const AXIS_VERTEX_SHADER = `
18
+ varying vec3 vWorldPos;
19
+
20
+ void main() {
21
+ vWorldPos = (modelMatrix * vec4(position, 1.0)).xyz;
22
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
23
+ }
24
+ `;
25
+
26
+ const AXIS_FRAGMENT_SHADER = `
27
+ uniform vec3 uColor;
28
+ uniform float uExtent;
29
+
30
+ varying vec3 vWorldPos;
31
+
32
+ void main() {
33
+ float axisDist = max(abs(vWorldPos.x), max(abs(vWorldPos.y), abs(vWorldPos.z)));
34
+ float vDist = axisDist / max(uExtent, 0.0001);
35
+
36
+ float alpha = 1.0 - smoothstep(0.06, 1.0, vDist);
37
+ if (alpha < 0.004) {
38
+ discard;
39
+ }
40
+
41
+ float core = 1.0 - smoothstep(0.0, 0.1, vDist);
42
+ vec3 color = mix(uColor, uColor * 1.12, core * 0.35);
43
+
44
+ gl_FragColor = vec4(color, alpha);
45
+ }
46
+ `;
47
+
48
+ function calcAxisExtent(camera: ViewCamera, origin = _origin) {
49
+ const distance = camera.position.distanceTo(origin);
50
+ if (!Number.isFinite(distance) || distance <= 0) {
51
+ return 50;
52
+ }
53
+
54
+ const visibleHeight = getViewHeightAtDistance(camera, distance);
55
+ const aspect = getViewAspect(camera);
56
+ const visibleWidth = visibleHeight * aspect;
57
+ const frustumSpan = Math.hypot(visibleWidth, visibleHeight);
58
+
59
+ const extent = frustumSpan * 2.5;
60
+ const farLimit = Number.isFinite(camera.far) && (camera.far ?? 0) > 0 ? (camera.far as number) * 0.98 : 1000;
61
+
62
+ return Math.min(farLimit, Math.max(extent, 20));
63
+ }
64
+
65
+ function createAxisLine(color: number) {
66
+ const geometry = new BufferGeometry();
67
+ geometry.setAttribute('position', new Float32BufferAttribute(new Float32Array(6), 3));
68
+
69
+ const material = new ShaderMaterial({
70
+ uniforms: {
71
+ uColor: { value: new Color(color) },
72
+ uExtent: { value: 1 },
73
+ },
74
+ vertexShader: AXIS_VERTEX_SHADER,
75
+ fragmentShader: AXIS_FRAGMENT_SHADER,
76
+ transparent: true,
77
+ depthTest: true,
78
+ depthWrite: false,
79
+ toneMapped: false,
80
+ });
81
+
82
+ const line = new Line(geometry, material);
83
+ line.renderOrder = 999;
84
+ line.frustumCulled = false;
85
+ line.raycast = () => {};
86
+ line.userData.objectType = HELPER_OBJECT_TYPE;
87
+ return line;
88
+ }
89
+
90
+ export class WorldAxes {
91
+ axes: Group | null = null;
92
+ lines: ThreeLine[] = [];
93
+ private camera: ViewCamera | null = null;
94
+
95
+ getObject() {
96
+ return this.axes;
97
+ }
98
+
99
+ create(camera: ViewCamera) {
100
+ this.camera = camera;
101
+
102
+ const x = createAxisLine(AXIS_X_COLOR);
103
+ x.userData.getEndpoints = (extent: number) => [-extent, 0, 0, extent, 0, 0];
104
+
105
+ const y = createAxisLine(AXIS_Y_COLOR);
106
+ y.userData.getEndpoints = (extent: number) => [0, -extent, 0, 0, extent, 0];
107
+
108
+ const z = createAxisLine(AXIS_Z_COLOR);
109
+ z.userData.getEndpoints = (extent: number) => [0, 0, -extent, 0, 0, extent];
110
+
111
+ this.lines = [x, y, z];
112
+
113
+ this.axes = new Group();
114
+ this.axes.add(...this.lines);
115
+ this.axes.userData.objectType = HELPER_OBJECT_TYPE;
116
+ return this.axes;
117
+ }
118
+
119
+ update() {
120
+ if (!this.camera) return;
121
+
122
+ const extent = calcAxisExtent(this.camera);
123
+ this.lines.forEach((line) => {
124
+ const positions = line.userData.getEndpoints(extent) as number[];
125
+ line.geometry.attributes.position.array.set(positions);
126
+ line.geometry.attributes.position.needsUpdate = true;
127
+ line.geometry.computeBoundingSphere();
128
+ (line.material as ShaderMaterial).uniforms.uExtent.value = extent;
129
+ });
130
+ }
131
+ }
@@ -0,0 +1,53 @@
1
+ import {
2
+ ACESFilmicToneMapping,
3
+ PCFSoftShadowMap,
4
+ PMREMGenerator,
5
+ SRGBColorSpace,
6
+ type Scene,
7
+ type Texture,
8
+ type WebGLRenderer,
9
+ } from 'three';
10
+ import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';
11
+
12
+ export const DEFAULT_TONE_MAPPING_EXPOSURE = 1;
13
+
14
+ /** Configure renderer color management, tone mapping, and shadows. */
15
+ export function configureRenderer(
16
+ renderer: WebGLRenderer,
17
+ options: { exposure?: number; pixelRatio?: number } = {},
18
+ ) {
19
+ renderer.outputColorSpace = SRGBColorSpace;
20
+ renderer.toneMapping = ACESFilmicToneMapping;
21
+ renderer.toneMappingExposure = Number.isFinite(options.exposure)
22
+ ? (options.exposure as number)
23
+ : DEFAULT_TONE_MAPPING_EXPOSURE;
24
+ renderer.shadowMap.enabled = true;
25
+ renderer.shadowMap.type = PCFSoftShadowMap;
26
+
27
+ if (Number.isFinite(options.pixelRatio)) {
28
+ renderer.setPixelRatio(options.pixelRatio as number);
29
+ }
30
+ }
31
+
32
+ /** Generate PMREM environment map from RoomEnvironment for scene IBL. */
33
+ export function createRoomEnvironmentMap(renderer: WebGLRenderer) {
34
+ const environment = new RoomEnvironment();
35
+ const pmremGenerator = new PMREMGenerator(renderer);
36
+ const envMap = pmremGenerator.fromScene(environment, 0.04).texture;
37
+ pmremGenerator.dispose();
38
+
39
+ return {
40
+ envMap,
41
+ dispose() {
42
+ envMap.dispose();
43
+ },
44
+ };
45
+ }
46
+
47
+ /** Apply environment map to scene (PBR reflections; does not replace background). */
48
+ export function applySceneEnvironment(scene: Scene, envMap: Texture | null, intensity = 1) {
49
+ scene.environment = envMap;
50
+ if ('environmentIntensity' in scene) {
51
+ (scene as Scene & { environmentIntensity: number }).environmentIntensity = intensity;
52
+ }
53
+ }