7.6 Example codes (51~60)

Click the Open Sandbox button. Then, you can run all the examples on this page by changing the value of the variable example in App.jsx.

import React, { useEffect, useRef, useState } from "react";
import {
  loadAudios,
  TextSprite,
  useFrame,
  useLoader,
  useRefEffect,
  useThree,
} from "threefy";
import { randomHSLColor, randomMatrix } from "./ThreeUtils";
import { Model } from "./ThreeModels";
import { Sky, SkyDome, Terrain } from "./ThreeNatures";
import { fitCameraToObject, randomCoords, randomHexColor } from "./ThreeUtils";
import * as THREE from "three";
import { FontLoader } from "three/examples/jsm/loaders/FontLoader";
import { DotScreenShader } from "three/examples/jsm/shaders/DotScreenShader";
import { RGBShiftShader } from "three/examples/jsm/shaders/RGBShiftShader";
import { FXAAShader } from "three/examples/jsm/shaders/FXAAShader";
import { GTAOPass } from "three/examples/jsm/postprocessing/GTAOPass";

import brickWall_diffuse_jpg from "../public/images/brickWall/diffuse.jpg";
import brickWall_normal_jpg from "../public/images/brickWall/normal.jpg";

import bathroom_diffuse_jpg from "../public/images/bathroom/diffuse.jpg";
import bathroom_normal_jpg from "../public/images/bathroom/normal.jpg";

import hdr_overcast_soil_puresky_2k_hdr from "../public/images/hdr/overcast_soil_puresky_2k.hdr";

import raymarch_abstract1_jpg from "../public/images/raymarch/abstract1.jpg";
import raymarch_pebbles_jpg from "../public/images/raymarch/pebbles.jpg";

import moss_diffuse_jpg from "../public/images/moss/diffuse.jpg";
import moss_normal_jpg from "../public/images/moss/normal.jpg";

import rockMossy_ao_jpg from "../public/images/rockMossy/ao.jpg";
import rockMossy_aoRoughMetal_jpg from "../public/images/rockMossy/aoRoughMetal.jpg";
import rockMossy_diffuse_jpg from "../public/images/rockMossy/diffuse.jpg";
import rockMossy_normal_jpg from "../public/images/rockMossy/normal.jpg";

import cloud_nx_png from "../public/images/background/cloud/nx.png";
import cloud_ny_png from "../public/images/background/cloud/ny.png";
import cloud_nz_png from "../public/images/background/cloud/nz.png";
import cloud_px_png from "../public/images/background/cloud/px.png";
import cloud_py_png from "../public/images/background/cloud/py.png";
import cloud_pz_png from "../public/images/background/cloud/pz.png";

import decal_diffuse_png from "../public/images/decal/diffuse.png";
import decal_normal_jpg from "../public/images/decal/normal.jpg";

import ratamahatta_md2_zip from "../public/models/ratamahatta_md2.zip";
import LeePerrySmith_glb from "../public/models/LeePerrySmith.glb";
import wild_town_zip from "../public/models/wild_town.zip";
import suzanne_glb from "../public/models/suzanne.glb";

import sample_mp3 from "../public/sounds/sample.mp3";

const DemoFog = () => {
  const { set } = useThree();
  set("camera.position.z", 300);

  const example = 2;

  if (example === 1) {
    const fogColor = 0xcccccc;
    const near = 1;
    const far = 1024;

    return (
      <>
        <Sky />
        <Terrain position-y={-200} />
        <fog args={[fogColor, near, far]} />
      </>
    );
  }
  if (example === 2) {
    const fogColor = 0xcccccc;
    const fogDensity = 0.002;

    return (
      <>
        <Sky />
        <Terrain position-y={-200} />
        <fogExp2 args={[fogColor, fogDensity]} />
      </>
    );
  }
};

const DemoPostProcessing = () => {
  const example = 7;

  if (example === 1) {
    return (
      <>
        <SkyDome />
        <Terrain position-y={-200} />
        <shaderPass args={[DotScreenShader]} uniforms-scale-value={3} />
        <shaderPass args={[RGBShiftShader]} uniforms-amount-value={0.0015} />
      </>
    );
  }
  if (example === 2) {
    const { get } = useThree();
    const [width, height] = get("width", "height");
    const pixelRatio = get("renderer.getPixelRatio")();
    const x = 1 / (width * pixelRatio);
    const y = 1 / (height * pixelRatio);

    return (
      <>
        <SkyDome />
        <Terrain position-y={-200} />
        <shaderPass
          args={[FXAAShader]}
          material-uniforms-resolution-value-x={x}
          material-uniforms-resolution-value-y={y}
        />
      </>
    );
  }
  if (example === 3) {
    return (
      <>
        <Sky />
        <Terrain position-y={-200} />
        <glitchPass />
      </>
    );
  }
  if (example === 4) {
    const ref = useRef(null);
    const { get } = useThree();
    const scene = get("scene");
    const camera = get("camera");
    const [width, height] = get("width", "height");

    const onLoad = (model) => {
      // fitCameraToObject( model );
      if (model.animations) {
        const animator = get("animator");
        animator.playAction(model, model.animations[0]);
      }
    };

    return (
      <>
        <Model url={wild_town_zip} onLoad={onLoad} scale={0.05} />
        <gTAOPass
          ref={ref}
          args={[scene, camera, width, height]}
          output={GTAOPass.OUTPUT.Denoise} // GTAOPass.OUTPUT.(Default|Diffuse|AO|Denoise|Depth|Normal)
          updateGtaoMaterial={{
            // aoParameters
            radius: 0.25,
            distanceExponent: 1.2,
            thickness: 2,
            scale: 3,
            samples: 16,
            distanceFallOff: 1,
            screenSpaceRadius: false,
          }}
          updatePdMaterial={{
            // pdParameters
            lumaPhi: 10,
            depthPhi: 2,
            normalPhi: 3,
            radius: 4,
            radiusExponent: 1,
            rings: 2,
            samples: 16,
          }}
        />
      </>
    );
  }
  if (example === 5) {
    const { get } = useThree();
    const scene = get("scene");
    const camera = get("camera");
    const [width, height] = get("width", "height");

    return (
      <>
        {randomCoords([50, 50, 50], 500, 3).map((p, i) => (
          <box
            key={i}
            scale={1 + 3 * Math.random()}
            position={p}
            type={"lambert"}
            color={randomHexColor()}
          />
        ))}
        <outlinePass args={[new THREE.Vector2(width, height), scene, camera]} />
      </>
    );
  }
  if (example === 6) {
    const { get, set } = useThree();
    const [width, height] = get("width", "height");

    const exposure = 1;
    set("renderer.toneMappingExposure", Math.pow(exposure, 4.0)); // exposure = 0.1 ~ 2

    const onLoad = (model) => {
      fitCameraToObject(model);
      if (model.animations) {
        const animator = get("animator");
        animator.playAction(model, model.animations[0]);
      }
    };

    return (
      <>
        <background color={"black"} />
        <Model url={ratamahatta_md2_zip} onLoad={onLoad} />
        <unrealBloomPass
          args={[new THREE.Vector2(width, height), 1.5, 0.4, 0.85]}
          threshold={0}
          strength={1}
          radius={0}
        />
      </>
    );
  }
  if (example === 7) {
    const { get, threefy } = useThree();
    const scene = get("scene");
    const camera = get("camera");
    const [width, height] = get("width", "height");

    camera.position.z = 500;

    const onLoad = (model) => {
      const suzanne = model.children[0];
      suzanne.visible = false;
      const material = suzanne.material;
      const geometry = suzanne.geometry;
      geometry.rotateX(Math.PI / 2);
      const group = new THREE.Group();
      const suzannes = 20;

      for (let i = 0; i < suzannes; i++) {
        const mesh = new THREE.Mesh(geometry, material);
        mesh.position.z = Math.cos((i / suzannes) * Math.PI * 2) * 200;
        mesh.position.y = Math.sin((i / suzannes) * Math.PI * 3) * 20;
        mesh.position.x = Math.sin((i / suzannes) * Math.PI * 2) * 200;
        mesh.rotation.y = (i / suzannes) * Math.PI * 2;
        mesh.scale.setScalar(20);
        group.add(mesh);
      }

      scene.add(group);
      group.update = (dt, ct) => (group.rotation.y += dt * 0.2);
    };

    const ref = useRef(null);

    useFrame((t) => {
      if (!ref.current) return;
      const bokehPass = ref.current;
      bokehPass.mouse = threefy.mouseMovePosition;
    });

    return (
      <>
        {/* <background url={hdr_overcast_soil_puresky_2k_hdr} /> */}
        <Model url={suzanne_glb} onLoad={onLoad} />
        <bokehPass
          ref={ref}
          args={[
            scene,
            camera,
            { width, height, vignetting: true, shaderFocus: true },
          ]}
        />
      </>
    );
  }
};

const DemoCurves = () => {
  // Curve (for math)
  // - LineCurve, LineCurve3
  // - EllipseCurve ==> ArcCurve
  // - CatmullRomCurve3
  // - SplineCurve
  // - QuadraticBezierCurve
  // - QuadraticBezierCurve3
  // - CubicBezierCurve
  // - CubicBezierCurve3
  //
  // Curve (hierarchy)
  // |- CurvePath (= curve array)
  //   |- Path(2d): provides methods for 2d shapes (eg: .moveTo().lineTo().bezierCurveTo())
  //     |- Shape(2d): defines a 2d shape plane using paths with (optional) holes
  //        1) Shape define the outerPath (ccw)
  //        2) Shape.holes define the holes in the shape (cw)
  //        3) used to create ShapeGeometry or ExtrudeGeometry
  // ShapePath (= path array)
  //
  // Line (for drawing)
  // |- LineLoop
  // |- LineSegments
  //
  // Steps:
  // 1) Curve/Path/Shape.getPoints() ==> points
  // 2) BufferGeometry.setFromPoints( Path.getPoints() )
  // 3) THREE.Line( BufferGeometry, LineBasicMaterial )
  //
  // Note*:
  // 1) Line.computeLineDistances(): needed for LineDashedMaterial
  // 2) LineBasicMaterial.linewidth = 1 (always)
  // 3) LineBasicMaterial.linecap(linejoin): ignored by WebGLRenderer

  const example = 4;

  if (example === 1) {
    const path = new THREE.Path();
    path.moveTo(0.5, 0.3);
    path.quadraticCurveTo(0.5, 0.5, 0.3, 0.5);
    path.lineTo(-0.3, 0.5);
    path.quadraticCurveTo(-0.5, 0.5, -0.5, 0.3);
    path.lineTo(-0.5, -0.3);
    path.quadraticCurveTo(-0.5, -0.5, -0.3, -0.5);
    path.lineTo(0.3, -0.5);
    path.quadraticCurveTo(0.5, -0.5, 0.5, -0.3);
    path.lineTo(0.5, 0.3);
    const points = path.getPoints();

    const ref = useRefEffect(() => {
      const line = ref.current;
      line.computeLineDistances();
    });

    return (
      <line ref={ref} scale={10}>
        <geometry type={"buffer"} setFromPoints={[points]} />
        <material
          type={"dashed"}
          color={0xffff00}
          dashSize={0.05}
          gapSize={0.05}
        />
      </line>
    );
  }

  if (example === 2) {
    const inputPoints = (format) => {
      // all format return the same points
      if (format === 1)
        return [
          new THREE.Vector3(-10, 0, 10),
          new THREE.Vector3(-5, 5, 5),
          new THREE.Vector3(0, 0, 0),
          new THREE.Vector3(5, -5, 5),
          new THREE.Vector3(10, 0, 10),
        ];
      else if (format === 2)
        return [
          [-10, 0, 10],
          [-5, 5, 5],
          [0, 0, 0],
          [5, -5, 5],
          [10, 0, 10],
        ];
      else if (format === 3)
        return [-10, 0, 10, -5, 5, 5, 0, 0, 0, 5, -5, 5, 10, 0, 10];
      else if (format === 4)
        return [
          { x: -10, y: 0, z: 10 },
          { x: -5, y: 5, z: 5 },
          { x: 0, y: 0, z: 0 },
          { x: 5, y: -5, z: 5 },
          { x: 10, y: 0, z: 10 },
        ];
    };

    const points = new THREE.CatmullRomCurve3(inputPoints(1)).getPoints(50);

    // (cf) two curves below are identical
    return (
      <>
        <line position-y={0} computeLineDistances={""}>
          <geometry type={"buffer"} setFromPoints={[points]} />
          <material
            type={"dashed"}
            dashSize={1}
            gapSize={0.5}
            color={"skyblue"}
          />
        </line>
        <catmullRom3
          position-y={2}
          args={[inputPoints(2)]}
          divisions={50}
          type={"dashed"}
          dashSize={1}
          gapSize={0.5}
          color={"red"}
        />
      </>
    );
  }

  if (example === 3) {
    return (
      <>
        <lineCurve
          dim={2} // 2d lineCurve
          args={[
            [0, 0],
            [10, 10],
          ]}
          type={"line"}
          color={"skyblue"}
        />
        <lineCurve
          dim={3} // 3d LineCurve3
          args={[
            [0, 0, 0],
            [-10, -10, -5],
          ]}
          type={"dashed"}
          color={"green"}
          dashSize={1}
          gapSize={0.5}
        />

        <arcCurve // <arcCurve/> is the same as <ellipseCurve/>
          args={[0, 0, 15, 10, 0, Math.PI * 2, false, Math.PI / 4]} // [aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation]
          divisions={50}
          type={"line"}
          color={"red"}
        />

        <bezierCurve
          order={"quadratic"}
          dim={"2"} // QuadraticBezierCurve
          args={[
            [-10, 0],
            [0, 15],
            [10, 0],
          ]}
          divisions={50}
          type={"line"}
          color={0xffff00}
        />
        <bezierCurve
          order={"cubic"}
          dim={3} // CubicBezierCurve3
          args={[
            [-10, 0, 0],
            [-5, 15, 0],
            [20, 15, 0],
            [10, 0, 0],
          ]}
          divisions={50}
          type={"dashed"}
          color={0xffffaa}
          dashSize={1}
          gapSize={0.5}
        />

        <splineCurve // <splineCurve dim={3}/> is the same as <catmullRom3/>
          args={[
            [
              [-10, 0],
              [0, 15],
              [10, 0],
            ],
          ]}
          divisions={50}
          type={"line"}
          color={0x0000ff}
        />
        <catmullRom3
          args={[
            [
              [-10, 0, 10],
              [-5, 5, 5],
              [0, 0, 0],
              [5, -5, 5],
              [10, 0, 10],
            ],
            false,
            "centripetal",
            0.5,
          ]} // [points, closed, curveType, tension]
          divisions={50}
          type={"dashed"}
          color={0x00aaff}
          dashSize={1}
          gapSize={0.5}
        />

        <nurbsCurve
          scale={4}
          args={[
            // degree
            2,
            // knots
            [0.0, 0.0, 0.0, 0.25, 0.25, 0.5, 0.5, 0.75, 0.75, 1.0, 1.0, 1.0],
            // controlPoints
            [
              [1.0, 0.0, 0.5, 1.0],
              [1.0, 1.0, 0.5, 0.707107],
              [0.0, 1.0, 0.5, 1.0],
              [-1.0, 1.0, 0.5, 0.707107],
              [-1.0, 0.0, 0.5, 1.0],
              [-1.0, -1.0, 0.5, 0.707107],
              [0.0, -1.0, 0.5, 1.0],
              [1.0, -1.0, 0.5, 0.707107],
              [1.0, 0.0, 0.5, 1.0],
            ],
          ]}
          divisions={50}
          type={"dashed"}
          color={0xfdcfaf}
          dashSize={0.1}
          gapSize={0.05}
        />
        <nurbsCurve
          scale={5}
          degree={2}
          knots={[
            0.0, 0.0, 0.0, 0.25, 0.25, 0.5, 0.5, 0.75, 0.75, 1.0, 1.0, 1.0,
          ]}
          controlPoints={[
            [1.0, 0.0, -0.5, 1.0],
            [1.0, 1.0, -0.5, 0.707107],
            [0.0, 1.0, -0.5, 1.0],
            [-1.0, 1.0, -0.5, 0.707107],
            [-1.0, 0.0, -0.5, 1.0],
            [-1.0, -1.0, -0.5, 0.707107],
            [0.0, -1.0, -0.5, 1.0],
            [1.0, -1.0, -0.5, 0.707107],
            [1.0, 0.0, -0.5, 1.0],
          ]}
          divisions={50}
          type={"dashed"}
          color={"skyblue"}
          dashSize={0.1}
          gapSize={0.05}
        />
      </>
    );
  }

  if (example === 4) {
    // the same result as example 3 (except for using 'Curve' component)
    return (
      <>
        <curve // LineCurve
          type={"line"} // 'line' can be omitted as default
          dim={2} // 2d LineCurve
          args={[
            [0, 0],
            [10, 10],
          ]}
          linetype={"solid"}
          color={"skyblue"}
        />
        <curve // LineCurve3
          type={"line"}
          dim={3} // 3d LineCurve3
          args={[
            [0, 0, 0],
            [-10, -10, -5],
          ]}
          linetype={"dashed"}
          color={"green"}
          dashSize={1}
          gapSize={0.5}
        />

        <curve // ArcCurve or EllipseCurve
          type={"arc"} // or 'ellipse'
          args={[0, 0, 15, 10, 0, Math.PI * 2, false, Math.PI / 4]}
          divisions={50}
          linetype={"solid"}
          color={"red"}
        />

        <curve // QuadraticBezierCurve
          type={"bezier"}
          order={"quadratic"}
          dim={"2"}
          args={[
            [-10, 0],
            [0, 15],
            [10, 0],
          ]}
          divisions={50}
          linetype={"solid"}
          color={0xffff00}
        />
        <curve // CubicBezierCurve3
          type={"bezier"}
          order={"cubic"}
          dim={3}
          args={[
            [-10, 0, 0],
            [-5, 15, 0],
            [20, 15, 0],
            [10, 0, 0],
          ]}
          divisions={50}
          linetype={"dashed"}
          color={0xffffaa}
          dashSize={1}
          gapSize={0.5}
        />

        <curve // SplineCurve
          type={"spline"}
          args={[
            [
              [-10, 0],
              [0, 15],
              [10, 0],
            ],
          ]}
          divisions={50}
          linetype={"solid"}
          color={0x0000ff}
        />
        <curve // CatmullRomCurve3
          type={"catmullRom3"}
          args={[
            [
              [-10, 0, 10],
              [-5, 5, 5],
              [0, 0, 0],
              [5, -5, 5],
              [10, 0, 10],
            ],
            false,
            "centripetal",
            0.5,
          ]}
          divisions={50}
          linetype={"dashed"}
          color={0x00aaff}
          dashSize={1}
          gapSize={0.5}
        />
        <curve // NURBSCurve
          type={"nurbs"}
          scale={4}
          args={[
            // degree
            2,
            // knots
            [0.0, 0.0, 0.0, 0.25, 0.25, 0.5, 0.5, 0.75, 0.75, 1.0, 1.0, 1.0],
            // controlPoints
            [
              [1.0, 0.0, 0.5, 1.0],
              [1.0, 1.0, 0.5, 0.707107],
              [0.0, 1.0, 0.5, 1.0],
              [-1.0, 1.0, 0.5, 0.707107],
              [-1.0, 0.0, 0.5, 1.0],
              [-1.0, -1.0, 0.5, 0.707107],
              [0.0, -1.0, 0.5, 1.0],
              [1.0, -1.0, 0.5, 0.707107],
              [1.0, 0.0, 0.5, 1.0],
            ],
          ]}
          divisions={50}
          linetype={"dashed"}
          color={0xfdcfaf}
          dashSize={0.1}
          gapSize={0.05}
        />
        <curve // NURBSCurve
          type={"nurbs"}
          scale={5}
          degree={2}
          knots={[
            0.0, 0.0, 0.0, 0.25, 0.25, 0.5, 0.5, 0.75, 0.75, 1.0, 1.0, 1.0,
          ]}
          controlPoints={[
            [1.0, 0.0, -0.5, 1.0],
            [1.0, 1.0, -0.5, 0.707107],
            [0.0, 1.0, -0.5, 1.0],
            [-1.0, 1.0, -0.5, 0.707107],
            [-1.0, 0.0, -0.5, 1.0],
            [-1.0, -1.0, -0.5, 0.707107],
            [0.0, -1.0, -0.5, 1.0],
            [1.0, -1.0, -0.5, 0.707107],
            [1.0, 0.0, -0.5, 1.0],
          ]}
          divisions={50}
          linetype={"dashed"}
          color={"skyblue"}
          dashSize={0.1}
          gapSize={0.05}
        />
      </>
    );
  }
};

const DemoShapes = () => {
  const [x, y] = [2, 0];
  const fishShape = new THREE.Shape()
    .moveTo(x, y)
    .quadraticCurveTo(x + 50, y - 80, x + 90, y - 10)
    .quadraticCurveTo(x + 100, y - 10, x + 115, y - 40)
    .quadraticCurveTo(x + 115, y, x + 115, y + 40)
    .quadraticCurveTo(x + 100, y + 10, x + 90, y + 10)
    .quadraticCurveTo(x + 50, y + 80, x, y);
  const holePath = new THREE.Path()
    .moveTo(x + 30, y + 0) // starting point of arc
    .absarc(x + 20, y + 0, 10, 0, Math.PI * 2, true);
  fishShape.holes.push(holePath);

  const heartShape = new THREE.Shape()
    .moveTo(x + 25, y + 25)
    .bezierCurveTo(x + 25, y + 25, x + 20, y, x, y)
    .bezierCurveTo(x - 30, y, x - 30, y + 35, x - 30, y + 35)
    .bezierCurveTo(x - 30, y + 55, x - 10, y + 77, x + 25, y + 95)
    .bezierCurveTo(x + 60, y + 77, x + 80, y + 55, x + 80, y + 35)
    .bezierCurveTo(x + 80, y + 35, x + 80, y, x + 50, y)
    .bezierCurveTo(x + 35, y, x + 25, y + 25, x + 25, y + 25);

  const curveSegments = 12;
  const extrudeSettings = {
    depth: 8,
    bevelEnabled: true,
    bevelSegments: 2,
    steps: 2,
    bevelSize: 1,
    bevelThickness: 1,
  };

  const texture = useLoader(raymarch_abstract1_jpg);
  texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
  texture.repeat.set(0.008, 0.008);

  const example = 1;

  if (example === 1) {
    return (
      <>
        <group position-y={10}>
          <mesh scale={0.2}>
            <geometry type={"shape"} args={[fishShape, curveSegments]} />
            <material
              type={"phong"}
              color={0x3a6b67}
              side={THREE.DoubleSide}
              map={texture}
            />
          </mesh>
          <mesh scale={0.05} position={[1.3, 7, 0]} rotation-z={Math.PI}>
            <geometry type={"shape"} args={[heartShape, curveSegments]} />
            <material type={"phong"} color={0xff0000} side={THREE.DoubleSide} />
          </mesh>
          <mesh scale={0.2} rotation-y={Math.PI}>
            <geometry type={"shape"} args={[fishShape, curveSegments]} />
            <material
              type={"phong"}
              color={0xffaaaa}
              side={THREE.DoubleSide}
              map={texture}
            />
          </mesh>
        </group>

        <group position-y={-10}>
          <mesh scale={0.2}>
            <geometry type={"extrude"} args={[fishShape, extrudeSettings]} />
            <material
              type={"phong"}
              color={0x3a6b67}
              side={THREE.DoubleSide}
              map={texture}
            />
          </mesh>
          <mesh scale={0.05} position={[1.3, 7, 0]} rotation-z={Math.PI}>
            <geometry type={"extrude"} args={[heartShape, extrudeSettings]} />
            <material type={"phong"} color={0xff0000} side={THREE.DoubleSide} />
          </mesh>
          <mesh scale={0.2} rotation-y={Math.PI}>
            <geometry type={"extrude"} args={[fishShape, extrudeSettings]} />
            <material
              type={"phong"}
              color={0xffaaaa}
              side={THREE.DoubleSide}
              map={texture}
            />
          </mesh>
        </group>
      </>
    );
  }

  if (example === 2) {
    return (
      <>
        <group position-y={10}>
          <shape
            scale={0.2}
            args={[fishShape, curveSegments]}
            type={"phong"}
            color={0x3a6b67}
            side={THREE.DoubleSide}
            map={texture}
          />
          <shape
            scale={0.05}
            position={[1.3, 7, 0]}
            rotation-z={Math.PI}
            args={[heartShape, curveSegments]}
            type={"phong"}
            color={0xff0000}
            side={THREE.DoubleSide}
          />
          <shape
            scale={0.2}
            rotation-y={Math.PI}
            args={[fishShape, curveSegments]}
            type={"phong"}
            color={0xffaaaa}
            side={THREE.DoubleSide}
            map={texture}
          />
        </group>

        <group position-y={-10}>
          <extrude
            scale={0.2}
            args={[fishShape, extrudeSettings]}
            type={"phong"}
            color={0x3a6b67}
            side={THREE.DoubleSide}
            map={texture}
          />
          <extrude
            scale={0.05}
            position={[1.3, 7, 0]}
            rotation-z={Math.PI}
            args={[heartShape, extrudeSettings]}
            type={"phong"}
            color={0xff0000}
            side={THREE.DoubleSide}
          />
          <extrude
            scale={0.2}
            rotation-y={Math.PI}
            args={[fishShape, extrudeSettings]}
            type={"phong"}
            color={0xffaaaa}
            side={THREE.DoubleSide}
            map={texture}
          />
        </group>
      </>
    );
  }

  if (example === 3) {
    return (
      <>
        <group position-y={10}>
          <line scale={0.2}>
            <geometry type={"buffer"} setFromPoints={[fishShape.getPoints()]} />
            <material type={"line"} color={0x3a6b67} />
          </line>
          <line scale={0.2}>
            <geometry
              type={"buffer"}
              setFromPoints={[fishShape.holes[0].getPoints()]}
            />
            <material type={"line"} color={0x3a6b67} />
          </line>

          <line scale={0.05} position={[1.3, 7, 0]} rotation-z={Math.PI}>
            <geometry
              type={"buffer"}
              setFromPoints={[heartShape.getPoints()]}
            />
            <material type={"line"} color={0xff0000} />
          </line>

          <line scale={0.2} rotation-y={Math.PI}>
            <geometry type={"buffer"} setFromPoints={[fishShape.getPoints()]} />
            <material type={"line"} color={0xffaaaa} />
          </line>
          <line scale={0.2} rotation-y={Math.PI}>
            <geometry
              type={"buffer"}
              setFromPoints={[fishShape.holes[0].getPoints()]}
            />
            <material type={"line"} color={0xffaaaa} />
          </line>
        </group>

        <group position-y={-10}>
          <points scale={0.2}>
            <geometry type={"buffer"} setFromPoints={[fishShape.getPoints()]} />
            <material type={"points"} color={0x3a6b67} />
          </points>
          <points scale={0.2}>
            <geometry
              type={"buffer"}
              setFromPoints={[fishShape.holes[0].getPoints()]}
            />
            <material type={"points"} color={0x3a6b67} />
          </points>

          <points scale={0.05} position={[1.3, 7, 0]} rotation-z={Math.PI}>
            <geometry
              type={"buffer"}
              setFromPoints={[heartShape.getPoints()]}
            />
            <material type={"points"} color={0xff0000} />
          </points>

          <points scale={0.2} rotation-y={Math.PI}>
            <geometry type={"buffer"} setFromPoints={[fishShape.getPoints()]} />
            <material type={"points"} color={0xffaaaa} />
          </points>
          <points scale={0.2} rotation-y={Math.PI}>
            <geometry
              type={"buffer"}
              setFromPoints={[fishShape.holes[0].getPoints()]}
            />
            <material type={"points"} color={0xffaaaa} />
          </points>
        </group>
      </>
    );
  }
};

const DemoCameraEffects = () => {
  const DemoInstMesh = () => {
    const instCount = 1000;

    const ref = useRefEffect((instMesh) => {
      for (let i = 0; i < instCount; i++) {
        const m = randomMatrix(40, 40, 40, 0.5, 1);
        const c = randomHSLColor();
        instMesh.setMatrixAt(i, m);
        instMesh.setColorAt(i, c);
      }
    });

    useFrame((t) => {
      if (ref.current) {
        ref.current.rotation.x = Math.sin(t * 1.2);
        ref.current.rotation.y = Math.sin(t * 2.4);
      }
    });

    return (
      <instancedMesh ref={ref} count={instCount}>
        <boxGeometry />
        <meshStandardMaterial />
      </instancedMesh>
    );
  };

  const example = 6;

  const { get } = useThree();
  const renderer = get("renderer");

  if (example === 1) {
    const [width, height] = get("width", "height");
    return (
      <>
        <DemoInstMesh />
        <anaglyphEffect args={[renderer, width, height]} />
      </>
    );
  }
  if (example === 2) {
    const ref = useRefEffect((effect) => {
      document.body.appendChild(effect.domElement);
    });
    return (
      <>
        <DemoInstMesh />
        <asciiEffect
          ref={ref}
          args={[renderer, " .:-+*=%@#", { invert: true }]}
          domElement-style-color={"white"}
          domElement-style-backgroundColor={"black"}
        />
      </>
    );
  }
  if (example === 3) {
    const defaultThickness = 0.003;
    const defaultColor = [0, 0, 0];
    const defaultAlpha = 1.0;
    const defaultKeepAlive = true;
    return (
      <>
        <DemoInstMesh />
        <outlineEffect
          args={[
            renderer,
            { defaultThickness, defaultColor, defaultAlpha, defaultKeepAlive },
          ]}
        />
      </>
    );
  }
  if (example === 4) {
    return (
      <>
        <DemoInstMesh />
        <parallaxBarrierEffect args={[renderer]} />
      </>
    );
  }
  if (example === 5) {
    return (
      <>
        <DemoInstMesh />
        <peppersGhostEffect args={[renderer]} />
      </>
    );
  }
  if (example === 6) {
    return (
      <>
        <DemoInstMesh />
        <stereoEffect args={[renderer]} />
      </>
    );
  }
};

const DemoAudio = () => {
  const example = 2;

  if (example === 1) {
    const createAudioButton = () => {
      const button = document.createElement("buttion");
      button.id = "audioButton";
      button.innerHTML = "Click To Play";
      button.style.cssText = `
                  position: absolute;
                  top: 50%;
                  left: 50%;
                  transform: translate(-50%,-50%);
                  color: #ffffff;
                  cursor: pointer;
              `;
      document.body.appendChild(button);
      return button;
    };
    const removeAudioButton = () => {
      document.getElementById("audioButton").remove();
    };

    const ref = useRefEffect((audio) => {
      const button = createAudioButton();

      button.addEventListener("click", () => {
        loadAudios(sample_mp3).then(async (buffer) => {
          audio.setBuffer(buffer);
          audio.play();

          removeAudioButton();
        });
      });
    });

    return (
      <audio
        ref={ref}
        args={[new THREE.AudioListener()]}
        setLoop={true}
        setVolume={0.2}
      />
    );
  }

  if (example === 2) {
    const createAudioButton = () => {
      const button = document.createElement("buttion");
      button.id = "audioButton";
      button.innerHTML = "Click To Play";
      button.style.cssText = `
                  position: absolute;
                  font-size: 16px;
                  top: 50%;
                  left: 50%;
                  transform: translate(-50%,-50%);
                  background: rgba(0,0,0,0.2);
                  border: 1px solid rgb(255, 255, 255);
                  border-radius: 8px;
                  color: #ffffff;
                  padding: 12px 18px;
                  text-transform: uppercase;
                  cursor: pointer;
              `;
      document.body.appendChild(button);
      return button;
    };
    const removeAudioButton = () => {
      document.getElementById("audioButton").remove();
    };
    const setAudioVisualizer = (audio) => {
      const fftSize = 128;
      const analyser = new THREE.AudioAnalyser(audio, fftSize);
      const material = new THREE.ShaderMaterial({
        depthWrite: false,
        transparent: true,
        side: THREE.DoubleSide,
        uniforms: {
          time: { value: 0.0 },
          tAudioData: {
            value: new THREE.DataTexture(
              analyser.data,
              fftSize / 2,
              1,
              THREE.RedFormat
            ),
          },
        },
        vertexShader: `
                      varying vec2 vUv;
                      void main() {
                          vUv = uv;
                          vec4 mvPosition = vec4( position, 1.0 );
                          gl_Position = projectionMatrix * modelViewMatrix * mvPosition;
                      }
                  `,
        fragmentShader: `
                      uniform sampler2D tAudioData;
                      uniform float time;
                      varying vec2 vUv;
                      void main() {
                          float f = texture2D( tAudioData, vec2( vUv.x, 0.0 ) ).r;
                          vec3 backColor = vec3( 0.0, vUv.x, vUv.y );
                          vec3 color = vec3( f, 0.5+0.5*sin(2.0*time), vUv.x );
                          float i = step( vUv.y, f );
                          float alpha = (1.0 - 0.2) * i + 0.2;
                          gl_FragColor = vec4( mix( backColor, color, i ), alpha );
                      }
                  `,
      });
      const geometry = new THREE.PlaneGeometry(1, 1);
      const mesh = new THREE.Mesh(geometry, material);
      mesh.position.z += 0.7;
      let oldfreq = 0;
      mesh.update = function (dt, ct) {
        // oscillate along the x-axis
        mesh.position.x = 0.01 * Math.sin(ct * 10);
        // oscillate along the y-axis
        const avgfreq = analyser.getAverageFrequency();
        mesh.position.y = 0.01 * (avgfreq - oldfreq);
        oldfreq = avgfreq;
        // update material.uniforms
        const uniforms = mesh.material.uniforms;
        analyser.getFrequencyData();
        uniforms["time"].value = ct;
        uniforms["tAudioData"].value.needsUpdate = true;
      };
      audio.add(mesh);
    };

    const ref = useRefEffect((audio) => {
      console.log(audio);
      const button = createAudioButton();

      button.addEventListener("click", () => {
        // NOTE*: audio.source
        // temporary used for audio source URLs
        // to be used as AudioNodes in the future
        loadAudios(audio.source).then(async (buffer) => {
          audio.setBuffer(buffer);
          audio.play();

          // audio.listener ==> camera
          useThree().camera.add(audio.listener);

          // audio helper: display the directional sound
          const { PositionalAudioHelper } = await import(
            "three/examples/jsm/helpers/PositionalAudioHelper"
          );
          audio.add(new PositionalAudioHelper(audio, 1.5));

          // audio visualizer: display the frequencies on plane
          setAudioVisualizer(audio);

          removeAudioButton();
        });
      });
    });

    return (
      <box
        scale={10}
        type={"lambert"}
        map={rockMossy_diffuse_jpg}
        normalMap={rockMossy_normal_jpg}
      >
        <positionalAudio
          ref={ref}
          args={[new THREE.AudioListener()]}
          setVolume={0.5}
          setLoop={true}
          setRefDistance={20} // reference distance at which the volume reduction starts taking effect
          setDirectionalCone={[180, 230, 0.1]} // define the directional sound
          source={sample_mp3} // temporary use for audio source URLs
        />
      </box>
    );
  }
};

const DemoLOD = () => {
  const example = 3;

  if (example === 1) {
    const ref = useRefEffect((lod) => {
      const material = new THREE.MeshBasicMaterial({ wireframe: true });
      const highDetail = new THREE.Mesh(
        new THREE.IcosahedronGeometry(10, 3),
        material
      );
      const midDetail = new THREE.Mesh(
        new THREE.IcosahedronGeometry(10, 2),
        material
      );
      const lowDetail = new THREE.Mesh(
        new THREE.IcosahedronGeometry(10, 1),
        material
      );

      lod.addLevel(highDetail, 0);
      lod.addLevel(midDetail, 75);
      lod.addLevel(lowDetail, 150);
    });

    return <lod ref={ref} />;
  }

  if (example === 2) {
    const ref = useRefEffect((lod) => {
      const distances = [0, 75, 150];
      lod.children.forEach((child, i) => {
        lod.levels.push({
          distance: distances[i],
          object: child,
          hysteresis: 0,
        });
      });
    });

    return (
      <lod ref={ref}>
        <icosahedron args={[10, 3]} type={"basic"} wireframe color={"red"} />
        <icosahedron args={[10, 2]} type={"basic"} wireframe color={"green"} />
        <icosahedron args={[10, 1]} type={"basic"} wireframe color={"blue"} />
      </lod>
    );
  }

  if (example === 3) {
    const level0 = new THREE.Mesh(
      new THREE.IcosahedronGeometry(10, 3),
      new THREE.MeshBasicMaterial({ wireframe: true, color: 0xff0000 })
    );
    const level1 = new THREE.Mesh(
      new THREE.IcosahedronGeometry(10, 2),
      new THREE.MeshBasicMaterial({ wireframe: true, color: 0x00ff00 })
    );
    const level2 = new THREE.Mesh(
      new THREE.IcosahedronGeometry(10, 1),
      new THREE.MeshBasicMaterial({ wireframe: true, color: 0x0000ff })
    );

    return (
      <lod
        addLevels={[
          [level0, 0], // high-level mesh at distance = 0
          [level1, 75], // middle-level mesh at distance = 75
          [level2, 150], // low-level mesh at distance = 150
        ]}
      />
    );
  }
};

const DemoGridHelper = () => {
  const size = 500;
  const divisions = 50;

  const gridXZCoords = (size, divisions) => {
    const coords = [];
    const spacing = size / divisions;
    const textHeight = spacing * 0.05;
    const x1 = size / 2,
      x0 = -x1;
    for (let x = x0; x <= x1; x += spacing) {
      const coord = x.toFixed(2);
      const coordX = new TextSprite(coord, {
        textHeight: textHeight,
        fontWeight: "bold",
      });
      const coordZ = coordX.clone();
      coordX.position.set(parseFloat(coord), textHeight / 2, 0);
      coordZ.position.set(0, textHeight / 2, parseFloat(coord));
      coords.push(coordX, coordZ);
    }
    return coords;
  };

  const ref = useRefEffect((gridHelper) => {
    const coords = gridXZCoords(size, divisions);
    gridHelper.add(...coords);
  });

  return <gridHelper ref={ref} args={[size, divisions, 0xff4444, 0x404040]} />;
};

const DemoBackground = () => {
  const example = 3;

  if (example === 1) {
    switch (1) {
      case 1:
        return <background color={"blue"} />;
      case 2:
        return <background color={0x0000ff} />;
      case 3:
        return <background color={[0, 0, 1]} />;
      case 4:
        return <background color={new THREE.Color(0, 0, 1)} />;
    }
  }

  if (example === 2) return <background url={raymarch_pebbles_jpg} />;

  if (example === 3)
    return (
      <background
        url={[
          cloud_px_png,
          cloud_nx_png,
          cloud_py_png,
          cloud_ny_png,
          cloud_pz_png,
          cloud_nz_png,
        ]}
      />
    );

  if (example === 4)
    return <background url={hdr_overcast_soil_puresky_2k_hdr} />;
};

const DemoAddonGeometries = () => {
  const example = 5;

  if (example === 1) {
    const getTestPoints = () => {
      const geometry = new THREE.IcosahedronGeometry(10);
      const posAttrib = geometry.getAttribute("position");
      const vertices = [];
      for (let i = 0; i < posAttrib.count; i++) {
        const vertex = new THREE.Vector3();
        vertex.fromBufferAttribute(posAttrib, i);
        vertices.push(vertex);
      }
      return vertices;
    };

    const points = getTestPoints();

    // 1) normal expression:
    // return <mesh>
    //     <geometry type={'convex'} args={[points]}/>
    //     <material type={'phong'} color={'yellow'} wireframe/>
    // </mesh>;

    // 2) abbreviated expression:
    return <convex args={[points]} type={"phong"} color={"yellow"} wireframe />;
  }

  if (example === 2) {
    const [step, setStep] = useState(0);

    const [args, setArgs] = useState({
      helper: new THREE.Mesh(new THREE.BoxGeometry(1, 1, 10)),
      position: new THREE.Vector3(),
      orientation: new THREE.Euler(),
      size: new THREE.Vector3(),
      color: new THREE.Color(),
      mesh: null,
    });

    const onClick = (e, mesh) => {
      const intersect = mesh.intersect;

      const { helper, position, orientation, size, color } = args;

      helper.position.copy(intersect.point);
      const n = intersect.face.normal.clone();
      n.transformDirection(mesh.matrixWorld);
      n.multiplyScalar(10);
      n.add(intersect.point);
      helper.lookAt(n);

      position.copy(intersect.point);

      orientation.copy(helper.rotation);
      orientation.z = Math.random() * 2 * Math.PI;

      const scale = 5 + Math.random() * (10 - 5);
      size.setScalar(scale);

      color.setHex(Math.random() * 0xffffff);

      setArgs({ helper, position, orientation, size, color, mesh });
      setStep(step + 1);
    };

    const onLoad = (model) => {
      const mesh = model.children[0];
      mesh.material.color.set(0xeec1ad); // skin_color = 0xf1c27d
      mesh.onClick = onClick;
      // (cf)
      // 1) first, model(= .glb) is loaded
      // 2) then, decal is created using onClick()
    };

    if (step === 0) {
      return <Model url={LeePerrySmith_glb} scale={5} onLoad={onLoad} />;
    }
    if (step > 0) {
      return (
        <decal
          // geometry
          args={[args.mesh, args.position, args.orientation, args.size]}
          // material
          type={"phong"}
          color={args.color}
          specular={0x444444}
          map={decal_diffuse_png}
          normalMap={decal_normal_jpg}
          normalScale={[1, 1]}
          shininess={30}
          transparent={true}
          depthTest={true}
          depthWrite={false}
          polygonOffset={true}
          polygonOffsetFactor={-4}
          wireframe={false}
        />
      );
    }
  }

  if (example === 3) {
    const surfaceFn = (u, v, target) => {
      const freq = Math.PI * 2 * 2;
      u = u * 2 - 1;
      v = v * 2 - 1;
      target.set(u, v, Math.cos(u * freq) * Math.sin(v * freq));
    };

    const ref = useRefEffect((model) => {
      model.geometry.attributes.uv2 = model.geometry.attributes.uv;
    });

    return (
      <parametric
        ref={ref}
        args={[surfaceFn, 64, 64]}
        scale={[15, 15, 1]}
        rotation-x={Math.PI / -2}
        type={"standard"}
        color={"white"}
        map={rockMossy_diffuse_jpg}
        normalMap={rockMossy_normal_jpg}
        aoMap={rockMossy_ao_jpg}
        roughnessMap={rockMossy_aoRoughMetal_jpg}
      />
    );
  }

  if (example === 4) {
    const [font, setFont] = useState(null);

    if (!font) {
      const loader = new FontLoader();
      // (cf) see https://github.com/mrdoob/three.js/tree/master/examples/fonts
      // fontName = helvetiker, optimer, gentilis, droid sans, droid serif
      // fontWeight = normal bold
      // loader.load(optimer_bold_typeface_json, (font) => setFont(font));
      loader.load("../public/fonts/optimer_bold.typeface.json", (font) =>
        setFont(font)
      );
    }

    const ref = useRef(null);

    let novaTex;
    if (font) {
      novaTex = useLoader(raymarch_abstract1_jpg);
      novaTex.wrapS = novaTex.wrapT = THREE.RepeatWrapping;
      novaTex.repeat.set(0.008, 0.008);

      useFrame((t) => {
        const group = ref.current;
        group.rotation.y = t * 0.4;

        const text = group.children[0];
        text.rotation.x = Math.sin(t * 2.5) * 0.2;
      });
    }

    useEffect(() => {
      const group = ref.current;
      if (group) {
        // 1st text
        let text = group.children[0];
        const g = text.geometry;
        g.computeBoundingBox();
        const xoffset = 1.0 * (g.boundingBox.max.x - g.boundingBox.min.x);
        const yoffset = 1.0 * (g.boundingBox.max.y - g.boundingBox.min.y);
        text.position.x = -xoffset;

        // 2nd text
        text = group.children[1];
        text.position.x = xoffset * 0.03;

        // 3rd text
        text = group.children[2];
        text.position.x = -xoffset * 0.975;
        text.position.y = yoffset * 1.25;
      }
    });

    return (
      font && (
        <group ref={ref} scale={0.1} position-y={-4}>
          {/* 1st text */}
          <text
            args={[
              "Nova",
              {
                font: font,
                size: 70,
                depth: 20,
                curveSegments: 4,
                bevelThickness: 2,
                bevelSize: 1.5,
                bevelEnabled: true,
              },
            ]}
            type={"standard"}
            map={novaTex}
            metalness={0.8}
            roughness={0.1}
          />
          {/* 2nd text */}
          <text
            args={[
              "Graphix",
              {
                font: font,
                size: 70,
                depth: 20,
                curveSegments: 4,
                bevelThickness: 2,
                bevelSize: 1.5,
                bevelEnabled: true,
              },
            ]}
            type={"standard"}
            color={0xbaff41}
            metalness={0.8}
            roughness={0.1}
          />
          {/* 3rd text */}
          <text
            args={[
              "https://www.nova-graphix.com",
              {
                font: font,
                size: 22,
                depth: 20,
                curveSegments: 4,
                bevelThickness: 2,
                bevelSize: 1.5,
                bevelEnabled: true,
              },
            ]}
            type={"standard"}
            color={0xffffff}
            metalness={0.8}
            roughness={0.1}
          />
          <background url={"images/background/leather.jpg"} />
        </group>
      )
    );
  }

  if (example === 5) {
    const bathrooms = useLoader([bathroom_diffuse_jpg, bathroom_normal_jpg]);
    for (let i = 0; i < 2; i++) {
      bathrooms[i].wrapS = bathrooms[i].wrapT = THREE.RepeatWrapping;
      bathrooms[i].repeat.set(2, 8);
    }
    bathrooms[0].colorSpace = THREE.SRGBColorSpace;

    const brickWalls = useLoader([brickWall_diffuse_jpg, brickWall_normal_jpg]);
    for (let i = 0; i < 2; i++) {
      brickWalls[i].wrapS = brickWalls[i].wrapT = THREE.RepeatWrapping;
      brickWalls[i].repeat.set(4, 1);
    }
    brickWalls[0].colorSpace = THREE.SRGBColorSpace;

    const mosss = useLoader([moss_diffuse_jpg, moss_normal_jpg]);
    for (let i = 0; i < 2; i++) {
      mosss[i].wrapS = mosss[i].wrapT = THREE.RepeatWrapping;
      mosss[i].repeat.set(1, 1);
    }
    mosss[0].colorSpace = THREE.SRGBColorSpace;

    const rockMossys = useLoader([
      rockMossy_diffuse_jpg,
      rockMossy_normal_jpg,
      rockMossy_aoRoughMetal_jpg,
    ]);
    for (let i = 0; i < 3; i++) {
      rockMossys[i].wrapS = rockMossys[i].wrapT = THREE.RepeatWrapping;
      rockMossys[i].repeat.set(4, 16);
    }
    rockMossys[0].colorSpace = THREE.SRGBColorSpace;

    const r = 5,
      h = 5,
      e = 0.1;
    const RR = 12; // major radius of a torus
    const rr = 2; // minor radius of a torus
    const Rmax = RR + rr,
      Rmin = RR - rr;

    const cylControlNet_01 = [
      [
        [r, -h, 0, 1],
        [r, -h, r, 0.7071],
        [0, -h, r, 1],
        [-r, -h, r, 0.7071],
        [-r, -h, 0, 1],
        [-r, -h, -r, 0.7071],
        [0, -h, -r, 1],
        [r, -h, -r, 0.7071],
        [r, -h, 0, 1],
      ],
      [
        [r, h, 0, 1],
        [r, h, r, 0.7071],
        [0, h, r, 1],
        [-r, h, r, 0.7071],
        [-r, h, 0, 1],
        [-r, h, -r, 0.7071],
        [0, h, -r, 1],
        [r, h, -r, 0.7071],
        [r, h, 0, 1],
      ],
    ];
    const cylControlNet_02 = [
      [
        [r, h, 0, 1],
        [r, -h, 0, 1],
      ],
      [
        [r, h, r, 0.7071],
        [r, -h, r, 0.7071],
      ],
      [
        [0, h, r, 1],
        [0, -h, r, 1],
      ],
      [
        [-r, h, r, 0.7071],
        [-r, -h, r, 0.7071],
      ],
      [
        [-r, h, 0, 1],
        [-r, -h, 0, 1],
      ],
      [
        [-r, h, -r, 0.7071],
        [-r, -h, -r, 0.7071],
      ],
      [
        [0, h, -r, 1],
        [0, -h, -r, 1],
      ],
      [
        [r, h, -r, 0.7071],
        [r, -h, -r, 0.7071],
      ],
      [
        [r, h, 0, 1],
        [r, -h, 0, 1],
      ],
    ];
    const sphControlNet = [
      [
        [e, -r, 0, 1],
        [e, -r, e, 0.7071],
        [0, -r, e, 1],
        [-e, -r, e, 0.7071],
        [-e, -r, 0, 1],
        [-e, -r, -e, 0.7071],
        [0, -r, -e, 1],
        [e, -r, -e, 0.7071],
        [e, -r, 0, 1],
      ],
      [
        [r, -r, 0, 0.7071],
        [r, -r, r, 0.5],
        [0, -r, r, 0.7071],
        [-r, -r, r, 0.5],
        [-r, -r, 0, 0.7071],
        [-r, -r, -r, 0.5],
        [0, -r, -r, 0.7071],
        [r, -r, -r, 0.5],
        [r, -r, 0, 0.7071],
      ],
      [
        [r, 0, 0, 1],
        [r, 0, r, 0.7071],
        [0, 0, r, 1],
        [-r, 0, r, 0.7071],
        [-r, 0, 0, 1],
        [-r, 0, -r, 0.7071],
        [0, 0, -r, 1],
        [r, 0, -r, 0.7071],
        [r, 0, 0, 1],
      ],
      [
        [r, r, 0, 0.7071],
        [r, r, r, 0.5],
        [0, r, r, 0.7071],
        [-r, r, r, 0.5],
        [-r, r, 0, 0.7071],
        [-r, r, -r, 0.5],
        [0, r, -r, 0.7071],
        [r, r, -r, 0.5],
        [r, r, 0, 0.7071],
      ],
      [
        [e, r, 0, 1],
        [e, r, e, 0.7071],
        [0, r, e, 1],
        [-e, r, e, 0.7071],
        [-e, r, 0, 1],
        [-e, r, -e, 0.7071],
        [0, r, -e, 1],
        [e, r, -e, 0.7071],
        [e, r, 0, 1],
      ],
    ];
    const torusControlNet = [
      [
        [RR, -rr, 0, 1],
        [RR, -rr, RR, 0.7071],
        [0, -rr, RR, 1],
        [-RR, -rr, RR, 0.7071],
        [-RR, -rr, 0, 1],
        [-RR, -rr, -RR, 0.7071],
        [0, -rr, -RR, 1],
        [RR, -rr, -RR, 0.7071],
        [RR, -rr, 0, 1],
      ],
      [
        [Rmax, -rr, 0, 0.7071],
        [Rmax, -rr, Rmax, 0.5],
        [0, -rr, Rmax, 0.7071],
        [-Rmax, -rr, Rmax, 0.5],
        [-Rmax, -rr, 0, 0.7071],
        [-Rmax, -rr, -Rmax, 0.5],
        [0, -rr, -Rmax, 0.7071],
        [Rmax, -rr, -Rmax, 0.5],
        [Rmax, -rr, 0, 0.7071],
      ],
      [
        [Rmax, 0, 0, 1],
        [Rmax, 0, Rmax, 0.7071],
        [0, 0, Rmax, 1],
        [-Rmax, 0, Rmax, 0.7071],
        [-Rmax, 0, 0, 1],
        [-Rmax, 0, -Rmax, 0.7071],
        [0, 0, -Rmax, 1],
        [Rmax, 0, -Rmax, 0.7071],
        [Rmax, 0, 0, 1],
      ],
      [
        [Rmax, rr, 0, 0.7071],
        [Rmax, rr, Rmax, 0.5],
        [0, rr, Rmax, 0.7071],
        [-Rmax, rr, Rmax, 0.5],
        [-Rmax, rr, 0, 0.7071],
        [-Rmax, rr, -Rmax, 0.5],
        [0, rr, -Rmax, 0.7071],
        [Rmax, rr, -Rmax, 0.5],
        [Rmax, rr, 0, 0.7071],
      ],
      [
        [RR, rr, 0, 1],
        [RR, rr, RR, 0.7071],
        [0, rr, RR, 1],
        [-RR, rr, RR, 0.7071],
        [-RR, rr, 0, 1],
        [-RR, rr, -RR, 0.7071],
        [0, rr, -RR, 1],
        [RR, rr, -RR, 0.7071],
        [RR, rr, 0, 1],
      ],
      [
        [Rmin, rr, 0, 0.7071],
        [Rmin, rr, Rmin, 0.5],
        [0, rr, Rmin, 0.7071],
        [-Rmin, rr, Rmin, 0.5],
        [-Rmin, rr, 0, 0.7071],
        [-Rmin, rr, -Rmin, 0.5],
        [0, rr, -Rmin, 0.7071],
        [Rmin, rr, -Rmin, 0.5],
        [Rmin, rr, 0, 0.7071],
      ],
      [
        [Rmin, 0, 0, 1],
        [Rmin, 0, Rmin, 0.7071],
        [0, 0, Rmin, 1],
        [-Rmin, 0, Rmin, 0.7071],
        [-Rmin, 0, 0, 1],
        [-Rmin, 0, -Rmin, 0.7071],
        [0, 0, -Rmin, 1],
        [Rmin, 0, -Rmin, 0.7071],
        [Rmin, 0, 0, 1],
      ],
      [
        [Rmin, -rr, 0, 0.7071],
        [Rmin, -rr, Rmin, 0.5],
        [0, -rr, Rmin, 0.7071],
        [-Rmin, -rr, Rmin, 0.5],
        [-Rmin, -rr, 0, 0.7071],
        [-Rmin, -rr, -Rmin, 0.5],
        [0, -rr, -Rmin, 0.7071],
        [Rmin, -rr, -Rmin, 0.5],
        [Rmin, -rr, 0, 0.7071],
      ],
      [
        [RR, -rr, 0, 1],
        [RR, -rr, RR, 0.7071],
        [0, -rr, RR, 1],
        [-RR, -rr, RR, 0.7071],
        [-RR, -rr, 0, 1],
        [-RR, -rr, -RR, 0.7071],
        [0, -rr, -RR, 1],
        [RR, -rr, -RR, 0.7071],
        [RR, -rr, 0, 1],
      ],
    ];

    const getControlPolygonLine = (controlPolygon) => {
      // create a line showing the control polygon of nurbs-curve
      const n = controlPolygon.length;
      let polygon = [];
      for (let i = 0; i < n; i++) {
        const c = controlPolygon[i];
        polygon.push(new THREE.Vector3(c[0], c[1], c[2]));
      }
      return new THREE.Line(
        new THREE.BufferGeometry().setFromPoints(polygon),
        new THREE.LineBasicMaterial({
          color: 0x333333,
          opacity: 0.25,
          transparent: true,
        })
      );
    };
    const getControlNetLines = (controlNet) => {
      // create lines showing the control net of nurbs-surface
      const lines = [];
      const nu = controlNet.length;
      const nv = controlNet[0].length;
      const m = new THREE.LineBasicMaterial({
        color: 0x333333,
        opacity: 0.25,
        transparent: true,
      });

      let polygon = [];
      for (let i = 0; i < nu; i++) {
        for (let j = 0; j < nv; j++) {
          const c = controlNet[i][j];
          polygon.push(new THREE.Vector3(c[0], c[1], c[2]));
        }
        const g = new THREE.BufferGeometry().setFromPoints(polygon);
        lines.push(new THREE.Line(g, m));
      }
      polygon = [];
      for (let j = 0; j < nv; j++) {
        for (let i = 0; i < nu; i++) {
          const c = controlNet[i][j];
          polygon.push(new THREE.Vector3(c[0], c[1], c[2]));
        }
        const g = new THREE.BufferGeometry().setFromPoints(polygon);
        lines.push(new THREE.Line(g, m));
      }

      return lines;
    };

    const cylRef_01 = useRefEffect((cyl) =>
      cyl.add(...getControlNetLines(cylControlNet_01))
    );
    const cylRef_02 = useRefEffect((cyl) =>
      cyl.add(...getControlNetLines(cylControlNet_02))
    );
    const sphRef = useRefEffect((sph) =>
      sph.add(...getControlNetLines(sphControlNet))
    );
    const torusRef = useRefEffect((torus) => {
      torus.geometry.attributes.uv2 = torus.geometry.attributes.uv;
      torus.add(...getControlNetLines(torusControlNet));
    });

    return (
      <>
        <nurbsSurface
          ref={cylRef_01}
          position-x={-r * 1.1}
          // nurbs definition
          args={[
            1, // u-degree
            2, // v-degree
            [0, 0, 1, 1], // u-knot
            [0, 0, 0, 0.25, 0.25, 0.5, 0.5, 0.75, 0.75, 1, 1, 1], // v-knot
            cylControlNet_01, // control points
          ]}
          // parametric geometry
          slices={2} // u = straight
          stacks={50} // v = circular
          // mesh material
          type={"standard"}
          side={THREE.DoubleSide}
          map={bathrooms[0]}
          normalMap={bathrooms[1]}
        />

        <nurbsSurface
          ref={cylRef_02}
          position-x={r * 1.1}
          // nurbs definition
          args={[
            2, // u-degree
            1, // v-degree
            [0, 0, 0, 0.25, 0.25, 0.5, 0.5, 0.75, 0.75, 1, 1, 1], // u-knot
            [0, 0, 1, 1], // v-knot
            cylControlNet_02, // control points
          ]}
          // parametric geometry
          slices={50} // u = circular
          stacks={2} // v = straight
          // mesh material
          type={"standard"}
          side={THREE.DoubleSide}
          map={brickWalls[0]}
          normalMap={brickWalls[1]}
        />

        <nurbsSurface
          ref={sphRef}
          position-y={r * 2}
          // nurbs definition
          args={[
            2, // u-degree
            2, // v-degree
            [0, 0, 0, 0.5, 0.5, 1, 1, 1], // u-knot
            [0, 0, 0, 0.25, 0.25, 0.5, 0.5, 0.75, 0.75, 1, 1, 1], // v-knot
            sphControlNet, // control points
          ]}
          // parametric geometry
          slices={25} // u = half-circular
          stacks={50} // v = circular
          // mesh material
          type={"standard"}
          map={mosss[0]}
          normalMap={mosss[1]}
        />

        <nurbsSurface
          ref={torusRef}
          position-y={-h * 1.5}
          // nurbs definition
          args={[
            2, // u-degree
            2, // v-degree
            [0, 0, 0, 0.25, 0.25, 0.5, 0.5, 0.75, 0.75, 1, 1, 1], // u-knot
            [0, 0, 0, 0.25, 0.25, 0.5, 0.5, 0.75, 0.75, 1, 1, 1], // v-knot
            torusControlNet, // control points
          ]}
          // parametric geometry
          slices={50} // u = circular
          stacks={50} // v = circular
          // mesh material
          type={"standard"}
          color={0xdfd29e}
          map={rockMossys[0]}
          normalMap={rockMossys[1]}
          aoMap={rockMossys[2]}
          roughnessMap={rockMossys[2]}
        />
      </>
    );
  }
};

const DemoExamples = ({ example }) => {
  switch (example) {
    case 51:
      return <DemoFog />;
    case 52:
      return <DemoPostProcessing />;
    case 53:
      return <DemoCurves />;
    case 54:
      return <DemoShapes />;
    case 55:
      return <DemoCameraEffects />;
    case 56:
      return <DemoAudio />;
    case 57:
      return <DemoLOD />;
    case 58:
      return <DemoGridHelper />;
    case 59:
      return <DemoBackground />;
    case 60:
      return <DemoAddonGeometries />;
  }
};

export { DemoExamples };

Last updated