three#Group TypeScript Examples

The following examples show how to use three#Group. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: FacePlayer.tsx    From spacesvr with MIT License 6 votes vote down vote up
export function FacePlayer(props: FacePlayerProps) {
  const { children, lockX = false, lockY = false, lockZ = false } = props;

  const group = useRef<Group>();
  const limiter = useLimiter(45);

  useFrame(({ clock, camera }) => {
    if (!limiter.isReady(clock)) return;

    if (group.current) {
      const prev = {
        x: group.current.rotation.x,
        y: group.current.rotation.y,
        z: group.current.rotation.z,
      };
      group.current.lookAt(camera.position);
      if (lockX) group.current.rotation.x = prev.x;
      if (lockY) group.current.rotation.y = prev.y;
      if (lockZ) group.current.rotation.z = prev.z;
    }
  });

  return (
    <group name="spacesvr-faceplayer" ref={group}>
      {children}
    </group>
  );
}
Example #2
Source File: Floating.tsx    From spacesvr with MIT License 6 votes vote down vote up
export function Floating(props: FloatingProps) {
  const { children, height = 0.2, speed = 1 } = props;

  const group = useRef<Group>();
  const seed = useRef(Math.random());
  const limiter = useLimiter(75);

  useFrame(({ clock }) => {
    if (!group.current || !limiter.isReady(clock)) return;

    group.current.position.y =
      height *
      Math.sin(clock.getElapsedTime() * speed * 0.4 + seed.current * 10000);
  });

  return (
    <group name="spacesvr-floating" ref={group}>
      {children}
    </group>
  );
}
Example #3
Source File: Spinning.tsx    From spacesvr with MIT License 6 votes vote down vote up
export function Spinning(props: Props) {
  const { children, xSpeed = 0, ySpeed = 1, zSpeed = 0 } = props;

  const group = useRef<Group>();
  const [seed] = useState(Math.random());
  const limiter = useLimiter(75);

  useFrame(({ clock }) => {
    if (!group.current || !limiter.isReady(clock)) return;

    group.current.rotation.x =
      clock.getElapsedTime() * xSpeed * 0.25 + xSpeed * seed * 100;
    group.current.rotation.y =
      clock.getElapsedTime() * ySpeed * (0.25 + seed / 10) +
      ySpeed * seed * 1000;
    group.current.rotation.z =
      clock.getElapsedTime() * zSpeed * 0.25 + zSpeed * seed * 40;
  });

  return (
    <group name="spacesvr-spinning" ref={group}>
      {children}
    </group>
  );
}
Example #4
Source File: MapView.ts    From geo-three with MIT License 6 votes vote down vote up
/**
	 * Ajust node configuration depending on the camera distance.
	 *
	 * Called everytime before render.
	 */
	public onBeforeRender: (renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, material: Material, group: Group)=> void = (renderer, scene, camera, geometry, material, group) => 
	{
		this.lod.updateLOD(this, camera, renderer, scene);
	};
Example #5
Source File: Tool.tsx    From spacesvr with MIT License 5 votes vote down vote up
/**
 * Tool modifier will place its children in constant view of the camera
 *
 * pos will determine relative placement on [x, y] axis
 * face will make item face the player (defaults to true)
 *
 * @param props
 * @constructor
 */
export function Tool(props: Props) {
  const { children, pos, face = true, pinY = false, distance = 1 } = props;

  const DISTANCE = distance * 0.05;

  const { size, camera } = useThree();

  const group = useRef<Group>();
  const groupPos = useRef(new Vector3());

  const { current: dummyVector } = useRef(new Vector3());

  useFrame(() => {
    if (!group.current) return;

    if (pos !== undefined) {
      const xPos = (pos[0] * 0.00008 * size.width) / 2;
      const yPos = 0.04 * pos[1];
      dummyVector.set(xPos * distance, yPos * distance, -DISTANCE);
      const moveQuaternion = camera.quaternion.clone();
      if (!pinY) {
        moveQuaternion.x = 0;
        moveQuaternion.z = 0;
      }
      dummyVector.applyQuaternion(moveQuaternion);

      group.current.getWorldPosition(groupPos.current);
      const deltaPos = groupPos.current.sub(camera.position);
      group.current.position.sub(deltaPos);
      group.current.position.add(dummyVector);
    }

    if (face) {
      group.current.quaternion.copy(camera.quaternion);
    }
  });

  return (
    <group name="spacesvr-tool">
      <group ref={group}>
        <group scale={SCALE * distance}>{children}</group>
      </group>
    </group>
  );
}
Example #6
Source File: MapView.d.ts    From geo-three with MIT License 5 votes vote down vote up
onBeforeRender: (renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, material: Material, group: Group) => void;
Example #7
Source File: TextMeshObject.ts    From movy with MIT License 4 votes vote down vote up
export default class TextMeshObject extends Group {
  initParams: TextMeshObjectParams;
  shouldUpdate = true;
  fonts: Font[];
  text: string;
  material: Material;

  constructor(params: TextMeshObjectParams = {}) {
    super();

    this.initParams = {
      centerTextVertically: false,
      color: new Color(0xffffff),
      font: 'en,zh',
      fontSize: 1.0,
      letterSpacing: 0,
      stroke: false,
      strokeWidth: 0.02,
      text3D: false,
      ...params,
    };

    if (this.initParams.material) {
      this.material = this.initParams.material.clone();
    } else {
      this.material = new MeshBasicMaterial({
        color: this.initParams.color,
        side: DoubleSide,
      });
    }
  }

  async init() {
    this.fonts = await Promise.all(
      this.initParams.font.split(',').map((fontName) => preloadFont(fontName))
    );
  }

  setText(text: string, forceUpdate = false) {
    this.text = text;
    this.shouldUpdate = true;
    if (forceUpdate) {
      this.updateText();
    }
  }

  updateText() {
    if (this.shouldUpdate) {
      // TODO: optimize: text update is slow.
      this.children.length = 0;

      let totalWidth = 0;
      const letterPosX: number[] = [];
      let minY = Number.MAX_VALUE;
      let maxY = Number.MIN_VALUE;
      const geometries: ShapeBufferGeometry[] = [];
      for (const [i, char] of [...this.text].entries()) {
        if (char === ' ') {
          totalWidth += this.initParams.fontSize * 0.5;
        } else {
          let font: Font;
          let glyph: any;
          for (let j = 0; j < this.fonts.length; j++) {
            font = this.fonts[j];
            glyph = (font.data as any).glyphs[char];
            if (glyph) {
              break;
            } else if (j == this.fonts.length - 1) {
              glyph = (font.data as any).glyphs['?'];
            }
          }

          const fontData = font.data as any;
          const resolution = fontData.resolution;
          const ha = (glyph.ha / resolution) * this.initParams.fontSize;

          const shapes = font.generateShapes(char, this.initParams.fontSize);

          let geometry;
          if (this.initParams.text3D) {
            const extrudeSettings = {
              depth: this.initParams.fontSize * 0.2,
              bevelEnabled: false,
            };
            geometry = new ExtrudeGeometry(shapes, extrudeSettings);
          } else if (this.initParams.stroke) {
            const style = SVGLoader.getStrokeStyle(
              this.initParams.strokeWidth,
              this.initParams.color.getStyle() // color in CSS context style
            );
            // Add shape.holes to shapes
            const holeShapes = [];
            for (let i = 0; i < shapes.length; i++) {
              const shape = shapes[i];
              if (shape.holes && shape.holes.length > 0) {
                for (let j = 0; j < shape.holes.length; j++) {
                  const hole = shape.holes[j];
                  holeShapes.push(hole);
                }
              }
            }
            shapes.push.apply(shapes, holeShapes);

            const geoms: BufferGeometry[] = [];
            for (const shape of shapes) {
              const points = shape.getPoints();
              const geom = SVGLoader.pointsToStroke(
                points.map((v) => new Vector3(v.x, v.y)),
                style
              );
              geoms.push(geom);
            }
            geometry = geoms.length > 1 ? mergeBufferGeometries(geoms) : geoms[0];
          } else {
            geometry = new ShapeBufferGeometry(shapes);
          }

          geometry.computeBoundingBox();

          geometries.push(geometry);

          // Always create a separate material for each letter
          const mesh = new Mesh(geometry, this.material.clone());
          mesh.name = char;

          const letterWidth = ha;
          const xMid = 0.5 * letterWidth;
          geometry.translate(
            -0.5 * (geometry.boundingBox.min.x + geometry.boundingBox.max.x),
            -0.5 * this.initParams.fontSize,
            0
          );

          letterPosX.push(totalWidth + xMid);
          totalWidth +=
            letterWidth +
            (i < this.text.length - 1
              ? this.initParams.letterSpacing * this.initParams.fontSize
              : 0);
          minY = Math.min(minY, geometry.boundingBox.min.y);
          maxY = Math.max(maxY, geometry.boundingBox.max.y);

          this.add(mesh);
        }
      }

      // Center text geometry vertically
      const deltaY = (maxY + minY) * 0.5;
      if (this.initParams.centerTextVertically) {
        for (const geometry of geometries) {
          geometry.translate(0, -deltaY, 0);
        }
      }

      this.children.forEach((letter, i) => {
        letter.position.set(-0.5 * totalWidth + letterPosX[i], 0, 0);
      });

      this.shouldUpdate = false;
    }
  }
}
Example #8
Source File: Interactable.tsx    From spacesvr with MIT License 4 votes vote down vote up
/**
 *
 * Interactable adds on click and hover methods to any group of Object3D's.
 *
 * This is a bit convoluted for the sake of working with the ClickDragControls
 * (i.e. the test for a double click)
 *
 * @param props
 * @constructor
 */
export function Interactable(props: Props) {
  const { onClick, onHover, onUnHover, children } = props;

  const gl = useThree((state) => state.gl);
  const { domElement } = gl;
  const { raycaster } = usePlayer();

  const group = useRef<Group>();
  const [hovered, setHovered] = useState(false);
  const { current: downPos } = useRef(new Vector2());
  const limiter = useLimiter(30);

  // continuously update the hover state
  useFrame(({ clock }) => {
    if (!group.current || !limiter.isReady(clock) || !raycaster) return;

    const intersections = raycaster.intersectObject(group.current, true);
    if (intersections && intersections.length > 0) {
      if (!hovered) {
        setHovered(true);
        if (onHover) {
          onHover();
        }
      }
    } else if (hovered) {
      setHovered(false);
      if (onUnHover) {
        onUnHover();
      }
    }
  });

  // start touch
  const onTouchStart = (e: TouchEvent) => {
    downPos.set(e.touches[0].clientX, e.touches[0].clientY);
  };

  // if little to no movement on touch end, call click event
  const onTouchEnd = (e: TouchEvent) => {
    const dist = downPos.distanceTo(
      new Vector2(e.changedTouches[0].clientX, e.changedTouches[0].clientY)
    );
    if (onClick && dist < 5 && hovered) {
      onClick();
    }
  };

  // set mouse down position
  const onMouseDown = (e: MouseEvent) => {
    downPos.set(e.clientX, e.clientY);
  };

  // if little to no movement on mouse up, queue a click event
  const onMouseUp = useCallback(
    (e: MouseEvent) => {
      const dist = downPos.distanceTo(new Vector2(e.clientX, e.clientY));
      if (onClick && dist < 5 && hovered) {
        onClick();
      }
    },
    [downPos, hovered]
  );

  useEffect(() => {
    if (isMobile) {
      domElement.addEventListener("touchstart", onTouchStart);
      domElement.addEventListener("touchend", onTouchEnd);
    } else {
      domElement.addEventListener("mousedown", onMouseDown);
      domElement.addEventListener("mouseup", onMouseUp);
    }

    return () => {
      if (isMobile) {
        domElement.removeEventListener("touchstart", onTouchStart);
        domElement.removeEventListener("touchend", onTouchEnd);
      } else {
        domElement.removeEventListener("mousedown", onMouseDown);
        domElement.removeEventListener("mouseup", onMouseUp);
      }
    };
  }, [onMouseUp, onTouchEnd, onClick]);

  return (
    <group name="spacesvr-interactable" ref={group}>
      {children}
    </group>
  );
}