7.5 Example codes (41~50)

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, { useRef } from "react";
import {
  CustomMaterial,
  mergeMeshes,
  MergedMesh,
  Mesh,
  useFrame,
  useRefEffect,
  useThree,
} from "threefy";
import {
  gridCoords,
  gridIndices,
  randomHSLColor,
  randomMatrix,
  transformMatrix,
} from "./ThreeUtils";
import { Model } from "./ThreeModels";
import {
  Grass,
  SimpleTree,
  Sky,
  SkyDome,
  Terrain,
  Water,
} from "./ThreeNatures";
import { Raymarch } from "./ThreeRaymarch";
import * as THREE from "three";

import crate_gif from "../public/images/crate.gif";
import bathroom_diffuse_jpg from "../public/images/bathroom/diffuse.jpg";
import terrain_Rugged_Terrain_diffuse_jpg from "../public/images/terrain/Rugged Terrain/diffuse.jpg";
import terrain_Rugged_Terrain_heightmap_jpg from "../public/images/terrain/Rugged Terrain/heightmap.jpg";
import raymarch_abstract1_jpg from "../public/images/raymarch/abstract1.jpg";
import raymarch_abstract2_jpg from "../public/images/raymarch/abstract2.jpg";
import raymarch_grayNoise256_png from "../public/images/raymarch/grayNoise256.png";
import raymarch_lichen_jpg from "../public/images/raymarch/lichen.jpg";
import raymarch_organic1_jpg from "../public/images/raymarch/organic1.jpg";
import raymarch_organic2_jpg from "../public/images/raymarch/organic2.jpg";
import raymarch_pebbles_jpg from "../public/images/raymarch/pebbles.jpg";
import raymarch_rgbaNoise256_png from "../public/images/raymarch/rgbaNoise256.png";
import raymarch_rockTiles_jpg from "../public/images/raymarch/rockTiles.jpg";
import water_normal_jpg from "../public/images/water/normal.jpg";

import leaf_ao_jpg from "../public/images/leaf/ao.jpg";
import leaf_diffuse_jpg from "../public/images/leaf/diffuse.jpg";
import leaf_displacement_png from "../public/images/leaf/displacement.png";
import leaf_normal_jpg from "../public/images/leaf/normal.jpg";
import leaf_roughness_jpg from "../public/images/leaf/roughness.jpg";

import wood_aoRoughMetal_jpg from "../public/images/wood/aoRoughMetal.jpg";
import wood_diffuse_jpg from "../public/images/wood/diffuse.jpg";
import wood_displacement_jpg from "../public/images/wood/displacement.jpg";
import wood_normal_jpg from "../public/images/wood/normal.jpg";

import moss_ao_jpg from "../public/images/moss/ao.jpg";
import moss_diffuse_jpg from "../public/images/moss/diffuse.jpg";
import moss_normal_jpg from "../public/images/moss/normal.jpg";
import moss_displacement_jpg from "../public/images/moss/displacement.jpg";
import moss_roughness_jpg from "../public/images/moss/roughness.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_displacement_jpg from "../public/images/rockMossy/displacement.jpg";
import rockMossy_normal_jpg from "../public/images/rockMossy/normal.jpg";

import gravel_diffuse_jpg from "../public/images/gravel/diffuse.jpg";
import gravel_displacement_jpg from "../public/images/gravel/displacement.jpg";
import gravel_normal_jpg from "../public/images/gravel/normal.jpg";
import gravel_aoRoughMetal_jpg from "../public/images/gravel/aoRoughMetal.jpg";

import jump_animation_glb from "../public/models/jump-animation.glb";
import ratamahatta_md2_zip from "../public/models/ratamahatta_md2.zip";
import wild_town_zip from "../public/models/wild_town.zip";

const DemoMergedMesh = () => {
  const { set } = useThree();
  // (cf)
  // set( 'camera.far', 5000 );      // possible
  // set( 'camera', { far: 5000 } ); // possible
  // set( { 'camera.far': 10000 } ); // possible

  const example = 1;

  let url;

  if (example === 1) {
    set("camera.far", 10000);
    url = wild_town_zip;
  }

  if (example === 2) {
    set("camera", { near: 0.01, far: 100 });
    set({ "camera.position.z": 5 });
    url = jump_animation_glb;
  }

  if (example === 3) {
    url = ratamahatta_md2_zip;
  }

  const ref = useRef(null);

  const onLoad = (model) => {
    // extract all meshes from model
    const meshes = [];
    model.traverse((object) => object.isMesh && meshes.push(object));

    // find the meshes with UV
    const meshes_with_uv = meshes.filter((mesh) =>
      mesh.geometry.hasAttribute("uv")
    );

    mergeMeshes(meshes_with_uv).then((mergMesh) => {
      const parent = model.parent;
      parent.remove(model);
      parent.add(mergMesh);
      ref.current = mergMesh;

      if (example === 1) {
        mergMesh.scale.setScalar(0.1);
      }
    });
  };

  return <Model ref={ref} url={url} onLoad={onLoad} />;
};

const DemoMergedMesh2 = () => {
  const [ni, nj, nk] = [20, 20, 20];
  // const [ ni, nj, nk ] = [ 30, 30, 30 ];
  // const [ ni, nj, nk ] = [ 40, 40, 40 ];
  const [hi, hj, hk] = [ni / 2, nj / 2, nk / 2];

  const example = 2;

  if (example === 1) {
    // slow rendering
    return (
      <>
        {gridIndices(ni, nj, nk).map(([i, j, k]) => (
          <box
            key={`${i},${j},${k}`}
            scale={0.7}
            position={[-hi + i, -hj + j, -hk + k]}
            map={crate_gif}
          />
        ))}
      </>
    );
  }

  if (example === 2) {
    // fast rendering
    return (
      <MergedMesh>
        {gridIndices(ni, nj, nk).map(([i, j, k]) => (
          <box
            key={`${i},${j},${k}`}
            scale={0.7}
            position={[-hi + i, -hj + j, -hk + k]}
            map={crate_gif} // or map={useLoader(crate_gif)}
          />
        ))}
      </MergedMesh>
    );
  }
};

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

  if (example === 1) {
    return (
      <MergedMesh>
        <box args={[10, 1, 10]} color={"tan"} map={crate_gif} />
        <box args={[5, 5, 5]} color={"skyblue"} />
        <box args={[1, 10, 1]} color={[1, 1, 0]} />
      </MergedMesh>
    );
  }

  if (example === 2) {
    const tx = 2;
    const trf = new THREE.Matrix4();
    const rot = new THREE.Matrix4()
      .premultiply(trf.makeRotationY(0.05))
      .premultiply(trf.makeRotationX(0.025));
    const scaleUp = new THREE.Matrix4()
      .premultiply(trf.makeTranslation(-tx, 0, 0))
      .premultiply(trf.makeScale(1.01, 1.01, 1.01))
      .premultiply(trf.makeTranslation(tx, 0, 0));
    const scaleDown = new THREE.Matrix4()
      .premultiply(trf.makeTranslation(-tx, 0, 0))
      .premultiply(trf.makeScale(0.99, 0.99, 0.99))
      .premultiply(trf.makeTranslation(tx, 0, 0));

    const ref = useRef(null);

    useFrame((t) => {
      const mergMesh = ref.current;

      if (mergMesh) {
        // instance id = 2
        mergMesh.getMatrixAt(2, trf);
        trf.premultiply(rot);
        mergMesh.setMatrixAt(2, trf);

        // instance id = 0
        mergMesh.getMatrixAt(0, trf);
        trf.premultiply(Math.sin(t * 10) > 0 ? scaleUp : scaleDown);
        mergMesh.setMatrixAt(0, trf);
      }
    });

    return (
      <MergedMesh ref={ref} scale={10}>
        {/* instance id = 0 */}
        <box position={[tx, 0, 0]} color={"tan"} map={crate_gif} />

        {/* instance id = 1 */}
        <mesh position={[-tx, 0, 0]}>
          <sphereGeometry args={[0.7]} />
          <meshBasicMaterial color={0xffaa99} map={raymarch_organic1_jpg} />
        </mesh>

        {/* instance id = 2 */}
        <Mesh>
          <geometry type={"box"} args={[1, 2, 1]} />
          <geometry type={"cylinder"} />
          <material type={"standard"} map={raymarch_abstract2_jpg} />
        </Mesh>
      </MergedMesh>
    );
  }
};

const DemoCompareMeshes = () => {
  const example = 4;

  //=====================================================
  // meshes: different geometry + different material
  //=====================================================
  if (example === 1) {
    // indexed geometries
    const types1 = [
      "box",
      "capsule",
      "circle",
      "cone",
      "cylinder",
      "lathe",
      "plane",
      "ring",
      "shape",
      "sphere",
      "torus",
      "torusKnot",
      "tube",
    ];
    // non-indexed geometries
    const types2 = [
      "dodecahedron",
      "extrude",
      "icosahedron",
      "octahedron",
      "tetrahedron",
    ]; // 'polyhedron' excluded why additional input is required

    const materials = [
      "basic",
      "lambert",
      "matcap",
      "phong",
      "standard",
      "physical",
      "toon",
    ];
    const texUrls = [
      raymarch_abstract1_jpg,
      raymarch_abstract2_jpg,
      raymarch_lichen_jpg,
      raymarch_organic1_jpg,
      raymarch_organic2_jpg,
      raymarch_pebbles_jpg,
      raymarch_rockTiles_jpg,
    ];

    const { threefy } = useThree();
    const textures = texUrls.map((url) => threefy.loadTexture(url));
    textures.forEach((tex) => (tex.colorSpace = THREE.SRGBColorSpace));

    const type1Count = types1.length;
    const type2Count = types2.length;
    const mtlCount = materials.length;
    const texCount = textures.length;

    const [n, dx, dim] = [10, 3, 3];

    return (
      <group>
        {/* meshes with 'indexed' geometry */}
        <group position={[-15, 0, 0]}>
          {gridCoords(n, dx, dim).map((p, i) => (
            <mesh key={i} scale={1} position={p}>
              <geometry type={types1[i % type1Count]} />
              <material
                type={materials[i % mtlCount]}
                map={textures[i % texCount]}
              />
            </mesh>
          ))}
        </group>

        {/* meshes with 'non-indexed' geometry */}
        <group position={[15, 0, 0]}>
          {gridCoords(n, dx, dim).map((p, i) => (
            <mesh key={i} scale={1} position={p}>
              <geometry type={types2[i % type2Count]} />
              <material
                type={materials[i % mtlCount]}
                map={textures[i % texCount]}
              />
            </mesh>
          ))}
        </group>
      </group>
    );
  }

  //=====================================================
  // instancedMesh: same geometry + same material
  //=====================================================
  if (example === 2) {
    const { threefy } = useThree();
    const texture1 = threefy.loadTexture(raymarch_abstract1_jpg);
    const texture2 = threefy.loadTexture(raymarch_abstract2_jpg);
    // texture1.colorSpace = THREE.SRGBColorSpace;
    // texture2.colorSpace = THREE.SRGBColorSpace;

    const count = 1000;

    const ref1 = useRefEffect((instMesh) => {
      for (let i = 0; i < count; i++) {
        const m = randomMatrix(30, 30, 30, 1, 1);
        instMesh.setMatrixAt(i, m);

        const c = randomHSLColor();
        instMesh.setColorAt(i, c);
      }
    });
    const ref2 = useRefEffect((instMesh) => {
      for (let i = 0; i < count; i++) {
        const m = randomMatrix(30, 30, 30, 1, 1);
        instMesh.setMatrixAt(i, m);

        const c = randomHSLColor();
        instMesh.setColorAt(i, c);
      }
    });

    return (
      <group>
        <instancedMesh
          ref={ref1}
          count={count}
          scale={1}
          position={[-15, 0, 0]}
        >
          <geometry type={"box"} />
          <material type={"standard"} map={texture1} />
        </instancedMesh>

        <instancedMesh ref={ref2} count={count} scale={1} position={[15, 0, 0]}>
          <geometry type={"sphere"} />
          <material type={"standard"} map={texture2} />
        </instancedMesh>
      </group>
    );
  }

  //=====================================================
  // batchedMesh = different geometry + same material
  //=====================================================
  if (example === 3) {
    // indexed geometries
    const types1 = [
      "box",
      "capsule",
      "circle",
      "cone",
      "cylinder",
      "lathe",
      "plane",
      "ring",
      "shape",
      "sphere",
      "torus",
      "torusKnot",
      "tube",
    ];
    // non-indexed geometries
    const types2 = [
      "dodecahedron",
      "extrude",
      "icosahedron",
      "octahedron",
      "tetrahedron",
    ]; // 'polyhedron' excluded why additional input is required

    const { threefy } = useThree();
    const indexTex = threefy.loadTexture(raymarch_abstract1_jpg);
    const nonindexTex = threefy.loadTexture(raymarch_abstract2_jpg);
    indexTex.colorSpace = THREE.SRGBColorSpace;
    nonindexTex.colorSpace = THREE.SRGBColorSpace;

    const createGeometries = (count, isIndexed) =>
      // count: total number of geometries
      // isIndexed: types1 if true, types2 if false
      {
        const types = isIndexed ? types1 : types2;
        const typeCount = types.length;

        return Array(count)
          .fill(0)
          .map((x, i) => {
            const type = types[i % typeCount];
            return <geometry key={i} type={type} attach={`geometries-${i}`} />;
          });
      };

    const [n, dx, dim] = [10, 3, 3];
    const count = Math.pow(n, dim);
    const posFn = (x, y, z, t) => [x, y, z];

    const composeBatchMesh = (batchMesh) => {
      const instIds = [];

      batchMesh.geometries.forEach((geometry) => {
        const geomId = batchMesh.addGeometry(geometry);
        const instId = batchMesh.addInstance(geomId);
        instIds.push(instId);
      });
      delete batchMesh.geometries;

      gridCoords(n, dx, dim).map((p, i) => {
        const trfm = transformMatrix([...p, 0], posFn);
        batchMesh.setMatrixAt(instIds[i], trfm);
      });
    };

    const ref1 = useRefEffect((batchMesh) => composeBatchMesh(batchMesh));
    const ref2 = useRefEffect((batchMesh) => composeBatchMesh(batchMesh));

    return (
      <group>
        {/* meshes with 'indexed' geometry */}
        <batchedMesh
          ref={ref1}
          args={[count, 6553600, 6553600 * 2]}
          position={[-15, 0, 0]}
        >
          {createGeometries(count, true)}
          <material type={"standard"} map={indexTex} />
        </batchedMesh>

        {/* meshes with 'non-indexed' geometry */}
        <batchedMesh
          ref={ref2}
          args={[count, 6553600, 6553600 * 2]}
          position={[15, 0, 0]}
        >
          {createGeometries(count, false)}
          <material type={"standard"} map={nonindexTex} />
        </batchedMesh>
      </group>
    );
  }

  //=====================================================
  // mergedMesh = different geometry + same material (but different texture)
  //=====================================================
  if (example === 4) {
    // indexed geometries
    const types1 = [
      "box",
      "capsule",
      "circle",
      "cone",
      "cylinder",
      "lathe",
      "plane",
      "ring",
      "shape",
      "sphere",
      "torus",
      "torusKnot",
      "tube",
    ];
    // non-indexed geometries
    const types2 = [
      "dodecahedron",
      "extrude",
      "icosahedron",
      "octahedron",
      "tetrahedron",
    ]; // 'polyhedron' excluded why additional input is required

    const materials = [
      "basic",
      "lambert",
      "matcap",
      "phong",
      "standard",
      "physical",
      "toon",
    ];
    const texUrls = [
      raymarch_abstract1_jpg,
      raymarch_abstract2_jpg,
      raymarch_lichen_jpg,
      raymarch_organic1_jpg,
      raymarch_organic2_jpg,
      raymarch_pebbles_jpg,
      raymarch_rockTiles_jpg,
    ];

    const { threefy } = useThree();
    const textures = texUrls.map((url) => threefy.loadTexture(url));

    const createMeshes = (n, dx, dim, scale, isIndexed) =>
      // n: # of meshes along each axis
      // dx: distance between meshes
      // dim: grid dimension
      // scale: mesh scale
      // isIndexed: types1 if true, types2 if false
      {
        const types = isIndexed ? types1 : types2;
        const typeCount = types.length;
        const mtlCount = materials.length;
        const texCount = textures.length;

        return gridCoords(n, dx, dim).map((p, i) => {
          const Type = types[i % typeCount];
          const mtl = materials[i % mtlCount];
          const tex = textures[i % texCount];

          if (dim === 1) {
            return (
              <Type
                key={i}
                scale={scale}
                position={[p, 0, 0]}
                type={mtl}
                map={tex}
              />
            );
          } else if (dim === 2) {
            return (
              <Type
                key={i}
                scale={scale}
                position={[p[0], p[1], 0]}
                type={mtl}
                map={tex}
              />
            );
          } else if (dim === 3) {
            return (
              <Type key={i} scale={scale} position={p} type={mtl} map={tex} />
            );
          }
        });
      };

    const ref1 = useRef(null);
    const ref2 = useRef(null);
    const [n, dx, dim, scale] = [10, 5, 3, 1.5];

    return (
      <group>
        {/* meshes with 'indexed' geometry */}
        <MergedMesh ref={ref1} position={[-25, 0, 0]}>
          {createMeshes(n, dx, dim, scale, true)}
        </MergedMesh>

        {/* meshes with 'non-indexed' geometry */}
        <MergedMesh ref={ref2} position={[25, 0, 0]}>
          {createMeshes(n, dx, dim, scale, false)}
        </MergedMesh>
      </group>
    );
  }
};

const DemoShaderMaterial = () => {
  const ref = useRef(null);

  useFrame((t) => {
    if (!ref.current) return;
    const mesh = ref.current;
    const uniforms = mesh.material.uniforms;
    uniforms["time"].value = t;
  });

  return (
    <box
      ref={ref}
      args={[40, 10, 10, 40, 10, 10]}
      type={"shader"}
      uniforms={{
        time: { value: 0.0 },
        textureMaps: {
          value: [
            raymarch_organic2_jpg,
            raymarch_abstract1_jpg,
            raymarch_pebbles_jpg,
            raymarch_lichen_jpg,
          ],
        },
      }}
      vertexShader={`
              varying vec3 vPos;
              varying vec2 vUv;
              uniform float time;
              void main()
              {
                  vec4 result = vec4( position.x, 1.0 * sin( position.x / 3.0 + time ) + position.y, position.z, 1.0 );
                  vPos = position;
                  vUv = uv;
                  gl_Position = projectionMatrix * (modelViewMatrix * result);
              }
          `}
      fragmentShader={`
              uniform sampler2D textureMaps[4];
              varying vec3 vPos;
              varying vec2 vUv;
              uniform float time;
              void main()
              {
                  float g = abs(mod( vPos.y, 2.0 ));
                  float b = abs(sin( 0.5*time ));
                  if(      vPos.x >  10.0 ) gl_FragColor = texture( textureMaps[0], vUv );
                  else if( vPos.x >   0.0 ) gl_FragColor = texture( textureMaps[1], vUv );
                  else if( vPos.x > -10.0 ) gl_FragColor = texture( textureMaps[2], vUv ) * vec4(1.0,g,b,1.0);
                  else                      gl_FragColor = texture( textureMaps[3], vUv );
              }
          `}
    />
  );
};

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

  const heightUrl = terrain_Rugged_Terrain_heightmap_jpg;
  const diffuseUrl = terrain_Rugged_Terrain_diffuse_jpg;
  const normalUrl = water_normal_jpg;

  const baseMaterialType = "standard"; // base material type (for custom-material)
  const py = -20,
    rx = -Math.PI / 2; // object(plane) transformation
  const args = [100, 100, 64, 64]; // geometry(plane) arguments
  const uniforms = {
    // uniforms used by custom-material
    height: { value: 70 },
    tHeight: { value: heightUrl },
    tDiffuse: { value: diffuseUrl },
  };

  // vertex declaration (injected to custom-material)
  const vdeclare = `
          uniform float height;
          uniform sampler2D tHeight;
          uniform sampler2D tDiffuse;
          varying vec2 vUv;
      `;

  // vertex transform (injected to custom-material)
  const vvertex = `
          float h = texture( tHeight, uv ).x;
          position = position + normal * h * height;
          vUv = uv;
      `;

  // fragment declaration (injected to custom-material)
  const fdeclare = `
          uniform sampler2D tHeight;
          uniform sampler2D tDiffuse;
          varying vec2 vUv;
      `;

  // fragment diffuse transform (injected to custom-material)
  const fdiffuse = `
          diffuse *= texture2D( tDiffuse, vUv ).rgb;
      `;

  // fragment color transform (injected to custom-material)
  const fcolor = `
          gl_FragColor.rgb *= texture2D( tDiffuse, vUv ).rgb;
      `;

  if (example === 1) {
    const material = new CustomMaterial(baseMaterialType, {
      normalMap: normalUrl,
      uniforms: uniforms,
      vdeclare: vdeclare,
      vvertex: vvertex,
      fdeclare: fdeclare,
      fdiffuse: fdiffuse,
      fcolor: fcolor,
    });

    const geometry = new THREE.PlaneGeometry(...args);

    const mesh = new THREE.Mesh(geometry, material);
    mesh.position.y = py;
    mesh.rotation.x = rx;

    return <primitive object={mesh} />;
  }

  if (example === 2) {
    return (
      <mesh rotation-x={rx} position-y={py}>
        <planeGeometry args={args} />
        <customMaterial
          type={baseMaterialType}
          normalMap={normalUrl}
          uniforms={uniforms}
          vdeclare={vdeclare}
          vvertex={vvertex}
          fdeclare={fdeclare}
          fdiffuse={fdiffuse}
          fcolor={fcolor}
        />
      </mesh>
    );
  }

  if (example === 3) {
    return (
      <plane
        rotation-x={rx}
        position-y={py}
        args={args}
        type={`custom-${baseMaterialType}`} // 'custom-standard' (*order important)
        normalMap={normalUrl}
        uniforms={uniforms}
        vdeclare={vdeclare}
        vvertex={vvertex}
        fdeclare={fdeclare}
        fdiffuse={fdiffuse}
        fcolor={fcolor}
      />
    );
  }
};

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

  if (example === 1) {
    // useful as background
    const fshader = "cloudsFS";
    let texture;

    switch (fshader) {
      case "cloudsFS":
        texture = raymarch_rgbaNoise256_png;
        break;
      case "cloudyFS":
      case "sunsetFS":
      case "cellsFS":
        break;
    }

    return <Raymarch fshader={fshader} textures={texture} />;
  }

  if (example === 2) {
    // raymarch based on shadertoy
    const fshader = "terrain2FS";
    let textures;

    switch (fshader) {
      case "iceFS":
        textures = [raymarch_pebbles_jpg];
        break;
      case "boxesFS":
        textures = [raymarch_pebbles_jpg];
        break;
      case "spheresFS":
        textures = [];
        break;
      case "spoutFS":
        textures = [bathroom_diffuse_jpg];
        break;
      case "terrainFS":
        textures = [raymarch_grayNoise256_png];
        break;
      case "terrain2FS":
        textures = [
          raymarch_lichen_jpg,
          raymarch_organic1_jpg,
          raymarch_grayNoise256_png,
          raymarch_lichen_jpg,
        ];
        break;
      case "tunnelFS":
        textures = [raymarch_lichen_jpg];
        break;
      case "galaxyFS":
        textures = [raymarch_pebbles_jpg];
        break;
      case "mountainsFS":
        textures = [raymarch_pebbles_jpg];
        break;
      case "canyonFS":
        textures = [
          raymarch_organic2_jpg,
          raymarch_abstract1_jpg,
          raymarch_rgbaNoise256_png,
        ];
        break;
      case "cavesFS":
        textures = [raymarch_pebbles_jpg, raymarch_organic1_jpg];
        break;
      case "seaFS":
        textures = [];
        break;
      case "weatherFS":
        textures = [raymarch_rgbaNoise256_png];
        break;
      case "riverFS":
        textures = [raymarch_grayNoise256_png, raymarch_organic1_jpg];
        break;
      case "forestFS":
        textures = [raymarch_grayNoise256_png];
        break;
      case "fishFS":
        textures = [
          raymarch_organic2_jpg,
          raymarch_abstract1_jpg,
          raymarch_grayNoise256_png,
          raymarch_lichen_jpg,
        ];
        break;
    }

    return <Raymarch fshader={fshader} textures={textures} />;
  }
};

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

  if (example === 1) {
    return <SkyDome />;
  }

  // if (example === 2) {
  //   return <Clouds />;
  // }

  if (example === 3) {
    return (
      <>
        <Sky />
        <Water waterSize={1024} />
      </>
    );
  }

  if (example === 4) {
    // return <plane
    //     rotation-x={Math.PI/-2}
    //     // position-y={py}
    //     args={[1024,1024,256,256]}
    //     type={'standard'}
    //     map={'images/terrain/Rugged Terrain/diffuse.jpg'}
    //     displacementMap={'images/terrain/Rugged Terrain/heightmap.jpg'}
    //     displacementScale={600}
    // />;
    return <Terrain />;
  }

  if (example === 5) {
    useThree().camera.position.set(0, 110, 200);
    return (
      <>
        <Grass width={256} numBlades={5000} />
        <SimpleTree scale={20} leafScale={20} />
      </>
    );
  }

  // if (example === 6) {
  //   return (
  //     <>
  //       <Grass width={256} numBlades={5000} color={0x3f6d21} />
  //       <ProcTree
  //         scale={20}
  //         twigDensity={7}
  //         twigColor={0x3f6d21}
  //         trunkColor={0x7c6344}
  //       />
  //     </>
  //   );
  // }
};

const DemoPBRmaterials = () => {
  const ref = useRefEffect((model) => {
    // (cf)
    //          uv2: required for aoMap, lightMap
    //        aoMap:   red channel is used
    // roughnessMap: green channel is used
    // metalnessMap:  blue channel is used
    model.geometry.attributes.uv2 = model.geometry.attributes.uv;
  });

  const example = "rockMossy";

  switch (example) {
    case "leaf":
      return (
        <sphere
          ref={ref}
          args={[16, 64, 32]}
          type={"standard"}
          map={leaf_diffuse_jpg}
          normalMap={leaf_normal_jpg}
          aoMap={leaf_ao_jpg}
          roughnessMap={leaf_roughness_jpg}
          displacementMap={leaf_displacement_png}
          displacementScale={2}
        />
      );
    case "wood":
      return (
        <sphere
          ref={ref}
          args={[16, 64, 32]}
          type={"standard"}
          map={wood_diffuse_jpg}
          normalMap={wood_normal_jpg}
          aoMap={wood_aoRoughMetal_jpg}
          roughnessMap={wood_aoRoughMetal_jpg}
          displacementMap={wood_displacement_jpg}
          displacementScale={2}
        />
      );
    case "moss":
      return (
        <sphere
          ref={ref}
          args={[16, 64, 32]}
          type={"standard"}
          map={moss_diffuse_jpg}
          normalMap={moss_normal_jpg}
          aoMap={moss_ao_jpg}
          roughnessMap={moss_roughness_jpg}
          displacementMap={moss_displacement_jpg}
          displacementScale={2}
        />
      );
    case "rockMossy":
      return (
        <sphere
          ref={ref}
          args={[16, 64, 32]}
          type={"standard"}
          map={rockMossy_diffuse_jpg}
          normalMap={rockMossy_normal_jpg}
          aoMap={rockMossy_ao_jpg}
          roughnessMap={rockMossy_aoRoughMetal_jpg}
          displacementMap={rockMossy_displacement_jpg}
          displacementScale={2}
        />
      );
    case "gravel":
      return (
        <sphere
          ref={ref}
          args={[16, 64, 32]}
          type={"standard"}
          map={gravel_diffuse_jpg}
          normalMap={gravel_normal_jpg}
          aoMap={gravel_aoRoughMetal_jpg}
          roughnessMap={gravel_aoRoughMetal_jpg}
          displacementMap={gravel_displacement_jpg}
          displacementScale={2}
        />
      );
  }
};

const DemoControls = () => {
  const { camera, renderer, scene } = useThree();

  const example = 9;

  // arcballControls
  if (example === 1) {
    return (
      <>
        <hemisphereLight />
        <sphere type={"standard"} scale={5} position-x={-10} color={0xfe828c} />
        <box type={"standard"} scale={10} position-x={10} color={"skyblue"} />
        <gridHelper args={[5000, 500, 0xff4444, 0x404040]} />
        <arcballControls args={[camera, renderer.domElement, scene]} />
      </>
    );
  }

  // dragControls
  if (example === 2) {
    return (
      <>
        <hemisphereLight />
        <sphere type={"standard"} scale={5} position-x={-10} color={"red"} />
        <box type={"standard"} scale={10} position-x={10} color={"skyblue"} />
        <gridHelper args={[5000, 500, 0xff4444, 0x404040]} />
        <dragControls args={[scene.children, camera, renderer.domElement]} />
      </>
    );
  }

  // firstPersonControls
  if (example === 3) {
    return (
      <>
        <hemisphereLight />
        <sphere type={"standard"} scale={5} position-x={-10} color={"red"} />
        <box type={"standard"} scale={10} position-x={10} color={"skyblue"} />
        <gridHelper args={[5000, 500, 0xff4444, 0x404040]} />
        <firstPersonControls
          args={[camera, renderer.domElement]}
          movementSpeed={10}
          lookSpeed={0.05}
        />
      </>
    );
  }

  // flyControls
  if (example === 4) {
    return (
      <>
        <hemisphereLight />
        <sphere type={"standard"} scale={5} position-x={-10} color={"red"} />
        <box type={"standard"} scale={10} position-x={10} color={"skyblue"} />
        <gridHelper args={[5000, 500, 0xff4444, 0x404040]} />
        <flyControls
          args={[camera, renderer.domElement]}
          movementSpeed={10}
          rollSpeed={0.2}
        />
      </>
    );
  }

  // mapControls
  if (example === 5) {
    return (
      <>
        <hemisphereLight />
        <sphere type={"standard"} scale={5} position-x={-10} color={"red"} />
        <box type={"standard"} scale={10} position-x={10} color={"skyblue"} />
        <gridHelper args={[5000, 500, 0xff4444, 0x404040]} />
        <mapControls
          args={[camera, renderer.domElement]}
          enableDamping={true}
        />
      </>
    );
  }

  // orbitControls
  if (example === 6) {
    return (
      <>
        <hemisphereLight />
        <sphere type={"standard"} scale={5} position-x={-10} color={"red"} />
        <box type={"standard"} scale={10} position-x={10} color={"skyblue"} />
        <gridHelper args={[5000, 500, 0xff4444, 0x404040]} />
        <orbitControls
          args={[camera, renderer.domElement]}
          enableDamping={true}
          dampingFactor={0.075}
        />
      </>
    );
  }

  // pointerLockControls
  if (example === 7) {
    return (
      <>
        <hemisphereLight />
        <sphere type={"standard"} scale={5} position-x={-10} color={"red"} />
        <box type={"standard"} scale={10} position-x={10} color={"skyblue"} />
        <gridHelper args={[5000, 500, 0xff4444, 0x404040]} />
        <pointerLockControls args={[camera, document.body]} />
      </>
    );
  }

  // trackballControls
  if (example === 8) {
    return (
      <>
        <hemisphereLight />
        <sphere type={"standard"} scale={5} position-x={-10} color={"red"} />
        <box type={"standard"} scale={10} position-x={10} color={"skyblue"} />
        <gridHelper args={[5000, 500, 0xff4444, 0x404040]} />
        <trackballControls
          args={[camera, renderer.domElement]}
          rotateSpeed={5}
        />
      </>
    );
  }

  // transformControls
  if (example === 9) {
    return (
      <>
        <hemisphereLight />
        <sphere type={"standard"} scale={5} position-x={-10} color={"red"} />
        <box type={"standard"} scale={10} position-x={10} color={"skyblue"} />
        <gridHelper args={[5000, 500, 0xff4444, 0x404040]} />
        <transformControls args={[camera, renderer.domElement]} />
      </>
    );
  }
};

const DemoExamples = ({ example }) => {
  switch (example) {
    case 41:
      return <DemoMergedMesh />;
    case 42:
      return <DemoMergedMesh2 />;
    case 43:
      return <DemoMergedMesh3 />;
    case 44:
      return <DemoCompareMeshes />;
    case 45:
      return <DemoShaderMaterial />;
    case 46:
      return <DemoCustomMaterial />;
    case 47:
      return <DemoRaymarchShaders />;
    case 48:
      return <DemoNatureShaders />;
    case 49:
      return <DemoPBRmaterials />;
    case 50:
      return <DemoControls />;
  }
};

export { DemoExamples };

Last updated