abstract-3d 2.7.3 → 2.7.5

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.
@@ -1,21 +1,26 @@
1
- import React from "react";
2
- import { extend } from "@react-three/fiber";
1
+ import React, { useMemo } from "react";
2
+ import { extend, useThree } from "@react-three/fiber";
3
3
  import { Text, Line } from "@react-three/drei";
4
4
  import {
5
5
  BoxGeometry,
6
6
  BufferAttribute,
7
+ BufferGeometry,
7
8
  CatmullRomCurve3,
9
+ Color,
8
10
  ConeGeometry,
9
11
  Curve,
10
12
  CylinderGeometry,
11
13
  Euler,
12
14
  ExtrudeGeometry,
15
+ Float32BufferAttribute,
16
+ FrontSide,
13
17
  Path,
14
18
  PlaneGeometry,
15
19
  Quaternion,
16
20
  Shape,
17
21
  SphereGeometry,
18
22
  TubeGeometry,
23
+ Vector2,
19
24
  Vector3,
20
25
  } from "three";
21
26
  import {
@@ -78,6 +83,31 @@ export function ReactMesh({
78
83
  readonly mesh: Mesh;
79
84
  readonly children?: React.JSX.Element;
80
85
  }): React.JSX.Element {
86
+ const culledLineGeometry = useMemo(() => {
87
+ const g = new BufferGeometry();
88
+
89
+ g.setAttribute(
90
+ "position",
91
+ new Float32BufferAttribute(
92
+ [
93
+ 0, -1, 0,
94
+ 0, 1, 0,
95
+ 1, -1, 0,
96
+ 1, 1, 0,
97
+ ],
98
+ 3
99
+ )
100
+ );
101
+
102
+ g.setIndex([
103
+ 0, 2, 1,
104
+ 2, 3, 1,
105
+ ]);
106
+
107
+ return g;
108
+ }, []);
109
+ const { camera, size } = useThree();
110
+
81
111
  switch (mesh.geometry.type) {
82
112
  case "Box": {
83
113
  const { pos, size, rot, holes } = mesh.geometry;
@@ -268,6 +298,27 @@ export function ReactMesh({
268
298
  </Line>
269
299
  );
270
300
  }
301
+ case "CulledLine": {
302
+ const { start, end, normal, thickness } = mesh.geometry;
303
+ const color = mesh.material.normal;
304
+
305
+ return <mesh geometry={culledLineGeometry} frustumCulled={false}>
306
+ <shaderMaterial
307
+ side={FrontSide}
308
+ uniforms={{
309
+ lineStart: { value: new Vector3(start.x, start.y, start.z) },
310
+ lineEnd: { value: new Vector3(end.x, end.y, end.z) },
311
+ lineNormal: { value: new Vector3(normal.x, normal.y, normal.z) },
312
+ thickness: { value: thickness },
313
+ viewport: { value: new Vector2(size.width, size.height) },
314
+ nearPlane: { value: camera.near },
315
+ color: { value: new Color(color) },
316
+ }}
317
+ vertexShader={culledLineVertexShader}
318
+ fragmentShader={culledLineFragmentShader}
319
+ />
320
+ </mesh>;
321
+ }
271
322
  case "Polygon":
272
323
  return <Polygon polygon={mesh.geometry}>{children}</Polygon>;
273
324
  case "Tube":
@@ -517,3 +568,91 @@ class CircleCurve extends Curve<Vector3> {
517
568
  );
518
569
  }
519
570
  }
571
+
572
+ const culledLineVertexShader = `
573
+ uniform vec3 lineStart;
574
+ uniform vec3 lineEnd;
575
+ uniform vec3 lineNormal;
576
+
577
+ uniform float thickness;
578
+ uniform float nearPlane;
579
+ uniform vec2 viewport;
580
+
581
+ void main() {
582
+ vec3 a = (modelViewMatrix * vec4(lineStart, 1.0)).xyz;
583
+ vec3 b = (modelViewMatrix * vec4(lineEnd, 1.0)).xyz;
584
+
585
+ vec3 n = normalize(mat3(modelViewMatrix) * lineNormal);
586
+
587
+ // camera is at (0,0,0) in view space.
588
+ vec3 mid = (a + b) * 0.5;
589
+ vec3 toCamera = normalize(-mid);
590
+
591
+ bool frontFacing = dot(n, toCamera) > 0.0;
592
+
593
+ if(!frontFacing) {
594
+ // collapse all 4 vertices to one point.
595
+ gl_Position = vec4(0.0);
596
+ return;
597
+ }
598
+
599
+ // ---- near-plane clipping ----
600
+
601
+ float nearZ = -nearPlane;
602
+
603
+ bool aBehind = a.z > nearZ;
604
+ bool bBehind = b.z > nearZ;
605
+
606
+ if(aBehind && bBehind) {
607
+ gl_Position = vec4(0.0);
608
+ return;
609
+ }
610
+
611
+ if(aBehind) {
612
+ float t = (nearZ - a.z) / (b.z - a.z);
613
+ a = mix(a, b, t);
614
+ }
615
+
616
+ if(bBehind) {
617
+ float t = (nearZ - b.z) / (a.z - b.z);
618
+ b = mix(b, a, t);
619
+ }
620
+
621
+ vec4 ca = projectionMatrix * vec4(a, 1.0);
622
+ vec4 cb = projectionMatrix * vec4(b, 1.0);
623
+
624
+ vec2 aNdc = ca.xy / ca.w;
625
+ vec2 bNdc = cb.xy / cb.w;
626
+
627
+ vec2 aPx = aNdc * viewport * 0.5;
628
+ vec2 bPx = bNdc * viewport * 0.5;
629
+
630
+ vec2 delta = bPx - aPx;
631
+ float len = length(delta);
632
+
633
+ if(len < 0.0001) {
634
+ gl_Position = vec4(0.0);
635
+ return;
636
+ }
637
+
638
+ vec2 dir = delta / len;
639
+ vec2 perp = vec2(-dir.y, dir.x);
640
+
641
+ float endpoint = position.x;
642
+ float side = position.y;
643
+
644
+ vec4 clip = mix(ca, cb, endpoint);
645
+
646
+ vec2 offsetNdc = perp * side * (thickness * 0.5) * (2.0 / viewport);
647
+ clip.xy += offsetNdc * clip.w;
648
+ gl_Position = clip;
649
+ }
650
+ `;
651
+
652
+ const culledLineFragmentShader = `
653
+ uniform vec3 color;
654
+
655
+ void main() {
656
+ gl_FragColor = vec4(color, 1.0);
657
+ }
658
+ `;
@@ -168,7 +168,7 @@ function svgMaskHoles(rot: Vec3, size: Vec2, holes: ReadonlyArray<Hole>): string
168
168
  );
169
169
  break;
170
170
  }
171
- case "SquareHole":
171
+ case "SquareHole": {
172
172
  const holeSizeRotated = vec3Rot(vec3(hole.size.x, hole.size.y, 0), vec3Zero, rot);
173
173
  const holeSize = vec2(Math.abs(holeSizeRotated.x), Math.abs(holeSizeRotated.y));
174
174
  const halfSize = vec2Scale(holeSize, 0.5);
@@ -178,6 +178,7 @@ function svgMaskHoles(rot: Vec3, size: Vec2, holes: ReadonlyArray<Hole>): string
178
178
  }" height="${holeSize.y / size.y}" fill="black" />`
179
179
  );
180
180
  break;
181
+ }
181
182
  default:
182
183
  break;
183
184
  }
@@ -1,10 +1,10 @@
1
- import { Line, Vec2, Vec3, vec3TransRot, vec3ZMean } from "../../../abstract-3d.js";
1
+ import { CulledLine, Line, Vec2, Vec3, vec3TransRot, vec3ZMean } from "../../../abstract-3d.js";
2
2
  import { SvgOptions, zElem, zOrderElement } from "./shared.js";
3
3
  import { svgLine } from "../svg-encoding.js";
4
4
  import { rgbGrayScale } from "../../../utils.js";
5
5
 
6
6
  export function line(
7
- l: Line,
7
+ l: Line | CulledLine,
8
8
  point: (x: number, y: number) => Vec2,
9
9
  fill: string,
10
10
  opts: SvgOptions,
@@ -165,6 +165,7 @@ function svgMesh(
165
165
  return cylinder(mesh.geometry, point, material, opts, parentPos, parentRot);
166
166
  case "Cone":
167
167
  return cone(mesh.geometry, point, material, opts, parentPos, parentRot);
168
+ case "CulledLine":
168
169
  case "Line":
169
170
  return line(mesh.geometry, point, material.normal, opts, parentPos, parentRot);
170
171
  case "Polygon":