7.4 Example codes (31~40)
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, useState } from "react";
import { useFrame, useLoader, useRefEffect, useThree } from "threefy";
import {
grid3D,
gridCoords,
randomHSLColor,
transformMatrix,
} from "./ThreeUtils";
import { InstancedModel, Model } from "./ThreeModels";
import { Grass, Sky, SkyDome, Terrain, Water } from "./ThreeNatures";
import * as THREE from "three";
import * as SkeletonUtils from "three/examples/jsm/utils/SkeletonUtils";
import crate_gif from "../public/images/crate.gif";
import brickWall_diffuse_jpg from "../public/images/brickWall/diffuse.jpg";
import moss_diffuse_jpg from "../public/images/moss/diffuse.jpg";
import hdr_overcast_soil_puresky_2k_hdr from "../public/images/hdr/overcast_soil_puresky_2k.hdr";
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 jump_animation_glb from "../public/models/jump-animation.glb";
import Cow_zip from "../public/models/Cow.zip";
import blackPearl_zip from "../public/models/blackPearl.zip";
import LeePerrySmith_glb from "../public/models/LeePerrySmith.glb";
const DemoModelLoading = () => {
const [model, setModel] = useState(null);
if (!model) {
const onLoad = (model) => setModel(model);
return <Model url={jump_animation_glb} onLoad={onLoad} />;
} else {
// remove model from scene
const removeFromScene = (model) => {
if (!model) return undefined;
const parent = model.parent;
if (parent) {
parent.remove(model);
model.parent = null;
}
return parent;
};
removeFromScene(model);
const dim = 3; //2;
const n = 10;
const dx = 2;
const x0 = (n / 2 - 0.5) * dx;
if (dim === 1) {
return (
<>
{grid1D(-x0, x0, dx).map((p, i) => (
<primitive
key={i}
object={SkeletonUtils.clone(model)}
position={[p, 0, 0]}
/>
))}
</>
);
}
if (dim === 2) {
return (
<>
{grid2D(-x0, x0, dx, -x0, x0, dx).map((p, i) => (
<primitive
key={i}
object={SkeletonUtils.clone(model)}
position={[p[0], 0, p[1]]}
/>
))}
</>
);
}
if (dim === 3) {
return (
<>
{grid3D(-x0, x0, dx, -x0, x0, dx, -x0, x0, dx).map((p, i) => (
<primitive
key={i}
object={SkeletonUtils.clone(model)}
position={[p[0], p[1], p[2]]}
/>
))}
</>
);
}
}
};
const DemoModelCreation = () => {
const DemoModelBox = () => {
const ref = useRef(null);
useFrame((t) => {
// (cf) ref.current: Object3D ==> Mesh
if (ref.current) ref.current.rotation.y = t;
});
return (
<Model
ref={ref}
object={{ type: "Mesh", name: "box" }}
geometry={{ type: "BoxGeometry", parameters: [20, 20, 20] }}
material={{ type: "MeshStandardMaterial", map: crate_gif }}
/>
);
};
const DemoModelSphere = () => {
const flakesTexture = (width = 512, height = 512) => {
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
const context = canvas.getContext("2d");
context.fillStyle = "rgb(127,127,255)";
context.fillRect(0, 0, width, height);
for (let i = 0; i < 4000; i++) {
const x = Math.random() * width;
const y = Math.random() * height;
const r = Math.random() * 3 + 3;
let nx = Math.random() * 2 - 1;
let ny = Math.random() * 2 - 1;
let nz = 1.5;
const l = Math.sqrt(nx * nx + ny * ny + nz * nz);
nx /= l;
ny /= l;
nz /= l;
context.fillStyle =
"rgb(" +
(nx * 127 + 127) +
"," +
(ny * 127 + 127) +
"," +
nz * 255 +
")";
context.beginPath();
context.arc(x, y, r, 0, Math.PI * 2);
context.fill();
}
return new THREE.CanvasTexture(canvas);
};
const scene = useThree((threefy) => threefy.scene);
const renderer = useThree((threefy) => threefy.renderer);
// flakes texture
const flakesTex = flakesTexture();
flakesTex.wrapS = THREE.RepeatWrapping;
flakesTex.wrapT = THREE.RepeatWrapping;
flakesTex.repeat.x = 10;
flakesTex.repeat.y = 6;
const normalTex = flakesTex;
// envMap texture
let envMap = undefined;
if (scene.environment) {
const envMapLoader = new THREE.PMREMGenerator(renderer);
envMap = envMapLoader.fromCubemap(scene.environment);
}
return (
<Model
object={{ type: "Mesh", name: "sphere" }}
geometry={{
type: "SphereGeometry",
parameters: [20, 64, 64],
}}
material={{
type: "MeshPhysicalMaterial",
clearcoat: 1.0,
clearcoatRoughness: 0.1,
clearcoatNormalMap: flakesTex,
clearcoatNormalScale: new THREE.Vector2(0.15, 0.15),
metalness: 0.9,
roughness: 0.5,
color: 0xff0000,
normalMap: normalTex,
normalScale: new THREE.Vector2(0.15, 0.15),
// envMap: envMap?.texture
}}
/>
);
};
const DemoModelPoints = () => {
const amount = 4096;
const range = 100;
const randFloatSpread = THREE.MathUtils.randFloatSpread;
const positions = [];
const colors = [];
for (let i = 0; i < amount; i++) {
const x = randFloatSpread(range);
const y = randFloatSpread(range);
const z = randFloatSpread(range);
positions.push(x, y, z);
}
for (let i = 0; i < amount; i++) {
const r = Math.random();
const g = Math.random();
const b = Math.random();
colors.push(r, g, b);
}
return (
<Model
object={{ type: "Points", name: "points" }}
geometry={{
type: "BufferGeometry",
attributes: {
position: positions,
color: colors,
},
}}
material={{
type: "PointsMaterial",
map: "images/sprites/metalBall.png", //'images/ball.png'
color: 0xffffff,
size: 3.0,
transparent: true,
depthTest: false,
vertexColors: true,
}}
/>
);
};
const DemoModelGeometries = () => {
const loader = new THREE.TextureLoader();
const diffuseMap = loader.load("images/brickWall/diffuse.jpg");
const normalMap = loader.load("images/brickWall/normal.jpg");
const model1 = {
object: { type: "Mesh", name: "box", position: [0, -20, 0] },
geometry: { type: "BoxGeometry", parameters: [10, 10, 10] }, // width, height, depth
material: {
type: "MeshStandardMaterial",
map: diffuseMap,
normalMap: normalMap,
},
};
const model2 = {
object: { type: "Mesh", name: "cylinder", position: [0, -10, 0] },
geometry: { type: "CylinderGeometry", parameters: [5, 5, 10, 32] }, // radiusTop, radiusBottom, height, radialSegments
material: {
type: "MeshStandardMaterial",
map: diffuseMap,
normalMap: normalMap,
},
};
const model3 = {
object: { type: "Mesh", name: "sphere", position: [0, 0, 0] },
geometry: { type: "SphereGeometry", parameters: [5, 32, 16] }, // radius, widthSegments, heightSegments
material: {
type: "MeshStandardMaterial",
map: diffuseMap,
normalMap: normalMap,
},
};
const model4 = {
object: { type: "Mesh", name: "torus", position: [0, 10, 0] },
geometry: { type: "TorusGeometry", parameters: [5, 1.5, 128, 32] }, // radius, tube, radialSegments(int), tubularSegments(int)
material: {
type: "MeshStandardMaterial",
map: diffuseMap,
normalMap: normalMap,
},
};
const model5 = {
object: { type: "Mesh", name: "torusKnot", position: [0, 20, 0] },
geometry: { type: "TorusKnotGeometry", parameters: [5, 1.5, 128, 32] }, // radius, tube, tubularSegments(int), radialSegments(int)
material: {
type: "MeshStandardMaterial",
map: diffuseMap,
normalMap: normalMap,
},
};
const model6 = {
object: {
type: "Sprite",
position: [10, 0, 0],
scale: [5, 5, 5],
name: "sprite",
},
material: {
type: "SpriteMaterial",
map: diffuseMap,
rotation: Math.PI / 4,
transparent: true,
opacity: 0.75,
depthWrite: false,
},
};
return <Model models={[model1, model2, model3, model4, model5, model6]} />;
};
const DemoModelShader = () => {
const vshader = `
varying vec3 vPos;
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;
gl_Position = projectionMatrix * (modelViewMatrix * result);
}
`;
const fshader = `
varying vec3 vPos;
uniform float time;
void main()
{
if( vPos.x >= 0.0 )
{
float r = 1.0;
float g = abs(mod( vPos.y, 4.0 ));
float b = abs(sin( time ));
gl_FragColor = vec4( r, g, b, 1.0 );
}
else
{
float r = 1.0;
float g = abs(cos( time ));
float b = abs(mod( vPos.z, 4.0 ));
gl_FragColor = vec4( r, g, b, 1.0 );
}
}
`;
const ref = useRef(null);
useFrame((t) => {
if (!ref.current) return;
// const group = ref.current;
// const uniforms = group.children[0].material.uniforms;
const mesh = ref.current;
const uniforms = mesh.material.uniforms;
uniforms["time"].value = t;
});
return (
<Model
ref={ref}
object={{ type: "Mesh", name: "shaderBox" }}
geometry={new THREE.BoxGeometry(60, 10, 10, 60, 10, 10)}
material={{
type: "ShaderMaterial",
wireframe: true,
uniforms: { time: { value: 0.0 } },
vertexShader: vshader,
fragmentShader: fshader,
}}
/>
);
};
const DemoModelTween = () => {
// NOTE*: tween required to import @tweenjs/tween.js
return (
<Model
object={{
type: "Mesh",
name: "box",
tween: [
{
from: { px: -30, rx: 0, ry: 0, sx: 1 },
to: { px: 30, rx: Math.PI * 0.7, ry: Math.PI * 0.4, sx: 2 },
dtime: 3000,
},
{
from: { px: 30, rx: Math.PI * 0.7, ry: Math.PI * 0.4, sx: 2 },
to: { px: -30, rx: 0, ry: 0, sx: 1 },
dtime: 3000,
cycling: true,
},
],
}}
geometry={{ type: "BoxGeometry", parameters: [10, 10, 10] }}
material={{ type: "MeshStandardMaterial", map: "images/crate.gif" }}
/>
);
};
const example = 1;
switch (example) {
case 1:
return <DemoModelBox />;
case 2:
return <DemoModelSphere />;
case 3:
return <DemoModelPoints />;
case 4:
return <DemoModelGeometries />;
case 5:
return <DemoModelShader />;
case 6:
return <DemoModelTween />;
}
};
const DemoGeometries = () => {
// material
const map = useLoader(brickWall_diffuse_jpg);
// EdgesGeometry
const sph = new THREE.SphereGeometry(0.75);
// Polyhedron
const verticesOfCube = [
-1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1,
-1, 1, 1,
];
const indicesOfFaces = [
2, 1, 0, 0, 3, 2, 0, 4, 7, 7, 3, 0, 0, 1, 5, 5, 4, 0, 1, 2, 6, 6, 5, 1, 2,
3, 7, 7, 6, 2, 4, 5, 6, 6, 7, 4,
];
const radius = 0.5,
detail = 2;
// TubeGeometry
class CustomSinCurve extends THREE.Curve {
constructor(scale = 1) {
super();
this.scale = scale;
}
getPoint(t, optionalTarget = new THREE.Vector3()) {
const tx = t * 3 - 1.5;
const ty = Math.sin(2 * Math.PI * t);
const tz = 0;
return optionalTarget.set(tx, ty, tz).multiplyScalar(this.scale);
}
}
const path = new CustomSinCurve(0.6);
// ShapeGeometry
const x = -0.2,
y = -0.8,
k = 0.08;
const heartShape = new THREE.Shape();
heartShape.moveTo(x + k * 5, y + k * 5);
heartShape.bezierCurveTo(x + k * 5, y + k * 5, x + k * 4, y, x, y);
heartShape.bezierCurveTo(
x - k * 6,
y,
x - k * 6,
y + k * 7,
x - k * 6,
y + k * 7
);
heartShape.bezierCurveTo(
x - k * 6,
y + k * 11,
x - k * 3,
y + k * 15.4,
x + k * 5,
y + k * 19
);
heartShape.bezierCurveTo(
x + k * 12,
y + k * 15.4,
x + k * 16,
y + k * 11,
x + k * 16,
y + k * 7
);
heartShape.bezierCurveTo(x + k * 16, y + k * 7, x + k * 16, y, x + k * 10, y);
heartShape.bezierCurveTo(
x + k * 7,
y,
x + k * 5,
y + k * 5,
x + k * 5,
y + k * 5
);
const Box = (props) => (
<mesh {...props}>
<boxGeometry />
<meshStandardMaterial map={map} />
</mesh>
);
const Capsule = (props) => (
<mesh {...props}>
<capsuleGeometry args={[0.5, 0.5]} />
<meshStandardMaterial map={map} />
</mesh>
);
const Circle = (props) => (
<mesh {...props}>
<circleGeometry args={[0.75]} />
<meshStandardMaterial map={map} side={THREE.DoubleSide} />
</mesh>
);
const Cone = (props) => (
<mesh {...props}>
<coneGeometry />
<meshStandardMaterial map={map} />
</mesh>
);
const Cylinder = (props) => (
<mesh {...props}>
<cylinderGeometry args={[0.75, 0.75]} />
<meshStandardMaterial map={map} />
</mesh>
);
const Dodecahedron = (props) => (
<mesh {...props}>
<dodecahedronGeometry />
<meshStandardMaterial map={map} />
</mesh>
);
const Extrude = (props) => (
<mesh {...props}>
<extrudeGeometry />
<meshStandardMaterial map={map} />
</mesh>
);
const Icosahedron = (props) => (
<mesh {...props}>
<icosahedronGeometry />
<meshStandardMaterial map={map} />
</mesh>
);
const Lathe = (props) => (
<mesh {...props}>
<latheGeometry />
<meshStandardMaterial map={map} />
</mesh>
);
const Octahedron = (props) => (
<mesh {...props}>
<octahedronGeometry />
<meshStandardMaterial map={map} />
</mesh>
);
const Plane = (props) => (
<mesh {...props}>
<planeGeometry />
<meshStandardMaterial map={map} side={THREE.DoubleSide} />
</mesh>
);
const Polyhedron = (props) => (
<mesh {...props}>
<polyhedronGeometry
args={[verticesOfCube, indicesOfFaces, radius, detail]}
/>
<meshStandardMaterial map={map} />
</mesh>
);
const Ring = (props) => (
<mesh {...props}>
<ringGeometry />
<meshBasicMaterial map={map} side={THREE.DoubleSide} />
</mesh>
);
const Shape = (props) => (
<mesh {...props}>
<shapeGeometry args={[heartShape]} />
<meshStandardMaterial map={map} side={THREE.DoubleSide} />
</mesh>
);
const Sphere = (props) => (
<mesh {...props}>
<sphereGeometry args={[0.75]} />
<meshStandardMaterial map={map} />
</mesh>
);
const Tetrahedron = (props) => (
<mesh {...props}>
<tetrahedronGeometry />
<meshStandardMaterial map={map} />
</mesh>
);
const Torus = (props) => (
<mesh {...props}>
<torusGeometry args={[0.5, 0.2]} />
<meshStandardMaterial map={map} />
</mesh>
);
const TorusKnot = (props) => (
<mesh {...props}>
<torusKnotGeometry args={[0.5, 0.2]} />
<meshStandardMaterial map={map} />
</mesh>
);
const Tube = (props) => (
<mesh {...props}>
<tubeGeometry args={[path, 20, 0.2, 8, false]} />
<meshStandardMaterial map={map} side={THREE.DoubleSide} />
</mesh>
);
const Edges = (props) => (
<lineSegments {...props}>
<edgesGeometry args={[sph]} />
<lineBasicMaterial color={0xaaaaaa} />
</lineSegments>
);
const Wireframe = (props) => (
<lineSegments {...props}>
<wireframeGeometry args={[sph]} />
<lineBasicMaterial
color={0xaaaaaa}
depthTest={false}
opacity={0.25}
transparent={true}
/>
</lineSegments>
);
const geomModels = [
Box,
Capsule,
Circle,
Cone,
Cylinder,
Dodecahedron,
Extrude,
Icosahedron,
Lathe,
Octahedron,
Plane,
Polyhedron,
Ring,
Shape,
Sphere,
Tetrahedron,
Torus,
TorusKnot,
Tube,
Edges,
Wireframe,
];
const refs = geomModels.map((x) => useRef(null));
useFrame((t) => {
if (!refs[0].current) return;
refs.forEach((ref) => {
const geomModel = ref.current;
geomModel.rotation.x = t * 3;
geomModel.rotation.y = t * 1.5;
});
});
return (
<group scale={5} position={[-12.5, -7.5, 0]}>
{geomModels.map((GeomModel, i) => (
<GeomModel
ref={refs[i]}
key={i}
scale={0.5}
position={[i % 6, parseInt(i / 6), 0]}
/>
))}
</group>
);
};
const DemoGeometries2 = () => {
const example = 2;
// material
const map = useLoader(brickWall_diffuse_jpg);
// Polyhedron
const verticesOfCube = [
-1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1,
-1, 1, 1,
];
const indicesOfFaces = [
2, 1, 0, 0, 3, 2, 0, 4, 7, 7, 3, 0, 0, 1, 5, 5, 4, 0, 1, 2, 6, 6, 5, 1, 2,
3, 7, 7, 6, 2, 4, 5, 6, 6, 7, 4,
];
const radius = 0.5,
detail = 2;
// TubeGeometry
class CustomSinCurve extends THREE.Curve {
constructor(scale = 1) {
super();
this.scale = scale;
}
getPoint(t, optionalTarget = new THREE.Vector3()) {
const tx = t * 3 - 1.5;
const ty = Math.sin(2 * Math.PI * t);
const tz = 0;
return optionalTarget.set(tx, ty, tz).multiplyScalar(this.scale);
}
}
const path = new CustomSinCurve(0.6);
// ShapeGeometry
const x = -0.2,
y = -0.8,
k = 0.08;
const heartShape = new THREE.Shape();
heartShape.moveTo(x + k * 5, y + k * 5);
heartShape.bezierCurveTo(x + k * 5, y + k * 5, x + k * 4, y, x, y);
heartShape.bezierCurveTo(
x - k * 6,
y,
x - k * 6,
y + k * 7,
x - k * 6,
y + k * 7
);
heartShape.bezierCurveTo(
x - k * 6,
y + k * 11,
x - k * 3,
y + k * 15.4,
x + k * 5,
y + k * 19
);
heartShape.bezierCurveTo(
x + k * 12,
y + k * 15.4,
x + k * 16,
y + k * 11,
x + k * 16,
y + k * 7
);
heartShape.bezierCurveTo(x + k * 16, y + k * 7, x + k * 16, y, x + k * 10, y);
heartShape.bezierCurveTo(
x + k * 7,
y,
x + k * 5,
y + k * 5,
x + k * 5,
y + k * 5
);
if (example === 1) {
const meshes = [],
helperLines = [];
const refs = Array(8)
.fill()
.map((x, i) => useRef(null));
const colors = Array(8)
.fill()
.map((x, i) => randomHSLColor().toArray());
let i = 0;
{
meshes.push(
<box
key={i}
ref={refs[i]}
scale={0.5}
position={[i % 3, parseInt(i / 3), 0]}
type={"basic"}
color={colors[i]}
map={map}
/>
);
i++;
meshes.push(
<cone
key={i}
ref={refs[i]}
scale={0.5}
position={[i % 3, parseInt(i / 3), 0]}
type={"phong"}
color={colors[i]}
map={map}
/>
);
i++;
meshes.push(
<cylinder
key={i}
ref={refs[i]}
args={[0.75, 0.75]}
scale={0.5}
position={[i % 3, parseInt(i / 3), 0]}
type={"standard"}
color={colors[i]}
map={map}
/>
);
i++;
meshes.push(
<icosahedron
key={i}
ref={refs[i]}
scale={0.5}
position={[i % 3, parseInt(i / 3), 0]}
type={"points"}
color={colors[i]}
map={map}
size={0.5}
/>
);
i++;
meshes.push(
<dodecahedron
key={i}
ref={refs[i]}
scale={0.5}
position={[i % 3, parseInt(i / 3), 0]}
type={"points"}
color={colors[i]}
map={map}
size={0.5}
/>
);
i++;
meshes.push(
<torusKnot
key={i}
ref={refs[i]}
args={[0.5, 0.2]}
scale={0.5}
position={[i % 3, parseInt(i / 3), 0]}
type={"points"}
color={colors[i]}
map={map}
size={0.5}
/>
);
i++;
helperLines.push(
<edges
key={i}
ref={refs[i]}
args={[new THREE.OctahedronGeometry()]}
scale={0.5}
position={[i % 3, parseInt(i / 3), 0]}
type={"line"}
color={colors[i]}
/>
);
i++;
helperLines.push(
<wireframe
key={i}
ref={refs[i]}
args={[new THREE.SphereGeometry(0.75)]}
scale={0.5}
position={[i % 3, parseInt(i / 3), 0]}
type={"line"}
color={colors[i]}
depthTest={false}
opacity={0.25}
transparent={true}
/>
);
i++;
}
useFrame((t) => {
if (!refs[0].current) return;
refs.forEach((ref, i) => {
const model = ref.current;
model.rotation.x = t * 1.5 * ((i % 3) + 1);
model.rotation.y = t * 0.8 * ((i % 4) + 1);
});
});
return (
<group scale={5} position={[-5, -5, 0]}>
{meshes}
{helperLines}
</group>
);
}
if (example === 2) {
const meshRefs = Array(19)
.fill()
.map((x, i) => useRef(null));
const lineRefs = Array(19)
.fill()
.map((x, i) => useRef(null));
const pointRefs = Array(19)
.fill()
.map((x, i) => useRef(null));
const refs = [...meshRefs, ...lineRefs, ...pointRefs];
const colors = Array(19)
.fill()
.map((x, i) => randomHSLColor().toArray());
const elems = [
"box",
"capsule",
"circle",
"cone",
"cylinder",
"dodecahedron",
"extrude",
"icosahedron",
"lathe",
"octahedron",
"plane",
"polyhedron",
"ring",
"shape",
"sphere",
"tetrahedron",
"torus",
"torusKnot",
"tube",
];
const args = [
[],
[0.5, 0.5],
[0.75],
[],
[0.75, 0.75],
[],
[],
[],
[],
[],
[],
[verticesOfCube, indicesOfFaces, radius, detail],
[],
[heartShape],
[0.75],
[],
[0.5, 0.2],
[0.5, 0.2],
[path, 20, 0.2, 8, false],
];
const sides = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1];
const types = [
"basic",
"lambert",
"matcap",
"normal",
"phong",
"physical",
"standard",
"toon",
"basic",
"lambert",
"matcap",
"normal",
"phong",
"physical",
"standard",
"toon",
"basic",
"lambert",
"matcap",
"normal",
];
useFrame((t) => {
if (!refs[0].current) return;
refs.forEach((ref, i) => {
const model = ref.current;
model.rotation.x = t * 1.5 * ((i % 3) + 1);
model.rotation.y = t * 0.8 * ((i % 4) + 1);
});
});
return (
<group>
{/* meshes */}
<group scale={5} position={[-8, -15, 0]}>
{elems.map((Elem, i) => (
<Elem
key={"mesh" + i}
ref={meshRefs[i]}
// geometry arguments
args={args[i]}
// material parameters
type={types[i]}
color={colors[i]}
map={map}
side={sides[i] ? THREE.DoubleSide : THREE.FrontSide}
// object3D properties
scale={0.5}
position={[i % 3, parseInt(i / 3), 0]}
/>
))}
</group>
{/* lines */}
<group scale={5} position={[-8 - 20, -15, 0]}>
{elems.map((Elem, i) => (
<Elem
key={"line" + i}
ref={lineRefs[i]}
// geometry arguments
args={args[i]}
// material parameters
type={"line"}
color={colors[i]}
map={map}
// object3D properties
scale={0.5}
position={[i % 3, parseInt(i / 3), 0]}
/>
))}
</group>
{/* points */}
<group scale={5} position={[-8 + 20, -15, 0]}>
{elems.map((Elem, i) => (
<Elem
key={"point" + i}
ref={pointRefs[i]}
// geometry arguments
args={args[i]}
// material parameters
type={"points"}
color={colors[i]}
map={map}
size={0.5}
// object3D properties
scale={0.5}
position={[i % 3, parseInt(i / 3), 0]}
/>
))}
</group>
</group>
);
}
};
const DemoOceanScene = () => {
const url = hdr_overcast_soil_puresky_2k_hdr;
const boxRef = useRef(null);
const waterRef = useRef(null);
useFrame((t) => {
// water
if (waterRef.current) {
const water = waterRef.current;
water.material.uniforms["time"].value += 1.0 / 60.0;
}
// box
if (boxRef.current) {
const box = boxRef.current;
const size = box.scale.x;
box.position.y = Math.sin(t) * (size * 0.7) + size * 0.2;
box.rotation.x = t * 0.5;
box.rotation.y = t * 0.51;
}
});
return (
<scene>
<background url={url} />
<hemisphereLight
args={[0x7cbfff, 0xffe5bc, 0.2 * Math.PI]}
position={[0, 1, 0]}
/>
<box ref={boxRef} scale={10} type={"standard"} map={crate_gif} />
<Water ref={waterRef} />
</scene>
);
};
const DemoShipOnTheSea = () => {
const url = hdr_overcast_soil_puresky_2k_hdr;
const waterRef = useRef(null);
const shipRef = useRef(null);
const ship = useLoader(blackPearl_zip);
let shipLoaded = false;
let vsize = new THREE.Vector3();
const onLoad = (ship) => {
shipRef.current = ship;
const materials = ship.children[0].material; // (cf) 9 materials
materials[3].side = THREE.DoubleSide;
shipLoaded = true;
const camera = useThree().camera;
camera.position.set(50, 15, 50);
};
useFrame((t) => {
// water
if (waterRef.current) {
const water = waterRef.current;
water.material.uniforms["time"].value += 1.0 / 60.0;
}
// ship
if (shipRef.current && shipLoaded) {
const ship = shipRef.current;
const mesh = ship.children[0];
if (!mesh.geometry.boundingBox) {
mesh.geometry.computeBoundingBox();
mesh.geometry.boundingBox.getSize(vsize);
}
const shipLength = vsize.z; // 459
const shipHeight = vsize.y; // 364
const shipWidth = vsize.x; // 170
const size = shipWidth * 0.05;
ship.position.y = Math.sin(t) * (size * 0.05) + size * 0.3;
ship.rotation.x = (Math.PI / 64) * Math.sin(t * 0.5); // x = pitching
ship.rotation.y = (Math.PI / 64) * Math.sin(t * 0.1); // y = yawing
ship.rotation.z = (Math.PI / 24) * Math.sin(t * 1.0); // z = rolling
}
});
return (
<scene>
<background url={url} />
<hemisphereLight
args={[0x7cbfff, 0xffe5bc, 0.2 * Math.PI]}
position={[0, 1, 0]}
/>
<primitive ref={shipRef} object={ship} scale={0.1} onLoad={onLoad} />
<Water ref={waterRef} />
</scene>
);
};
const DemoSkyTerrain = () => {
// choose one of the following terrains
const terrains = [];
terrains.push({
mapURL: terrain_Rugged_Terrain_diffuse_jpg,
heightURL: terrain_Rugged_Terrain_heightmap_jpg,
bumpURL: "",
size: [500, 300, 500],
altitude: -100,
});
// terrains.push({
// mapURL: "images/terrain/Great Lakes/diffuse.jpg",
// heightURL: "images/terrain/Great Lakes/heightmap.jpg",
// bumpURL: "",
// size: [500, 500, 500],
// altitude: -30,
// });
// terrains.push({
// mapURL: "images/terrain/Grand Mountain/diffuse.jpg",
// heightURL: "images/terrain/Grand Mountain/heightmap.jpg",
// bumpURL: "images/terrain/Grand Mountain/bump.jpg",
// size: [500, 300, 500],
// altitude: -20,
// });
// terrains.push({
// mapURL: "images/terrain/Rolling Hills/diffuse.jpg",
// heightURL: "images/terrain/Rolling Hills/heightmap.jpg",
// bumpURL: "",
// size: [500, 200, 500],
// altitude: -20,
// });
// terrains.push({
// mapURL: "images/terrain/Irvine Lake/diffuse.jpg",
// heightURL: "images/terrain/Irvine Lake/heightmap.jpg",
// bumpURL: "",
// size: [500, 100, 500],
// altitude: -30,
// });
const terrain = terrains[0];
// update sky and sunPosition
const skyRef = useRefEffect((sky) => {
const { scene, renderer } = useThree();
// sunPosition <== (azimuth, elevation)
const sunElevation = 2; // vertical angular toward the y (up) axis
const sunAzimuth = 180; // clockwise angle between North (North=0, East=90, South=180, West=270)
const phi = THREE.MathUtils.degToRad(90 - sunElevation); // polar angle from the y (up) axis
const theta = THREE.MathUtils.degToRad(sunAzimuth); // equator angle around the y (up) axis
const sunPosition = new THREE.Vector3().setFromSphericalCoords(
1,
phi,
theta
);
sky.material.uniforms["sunPosition"].value.copy(sunPosition);
// scene.environment <== sky
const sceneEnv = new THREE.Scene();
const pmremGenerator = new THREE.PMREMGenerator(renderer);
sceneEnv.add(sky);
const renderTarget = pmremGenerator.fromScene(sceneEnv);
scene.add(sky);
scene.environment = renderTarget.texture;
});
return (
<scene>
<Sky ref={skyRef} />
<Terrain
position-y={terrain.altitude}
xsize={terrain.size[0]}
ysize={terrain.size[1]}
zsize={terrain.size[2]}
map={terrain.mapURL}
heightMap={terrain.heightURL}
bumpMap={terrain.bumpURL}
/>
</scene>
);
};
const DemoGrassScene = () => {
const url = hdr_overcast_soil_puresky_2k_hdr;
const terrain = {
mapURL: terrain_Rugged_Terrain_diffuse_jpg,
heightURL: terrain_Rugged_Terrain_heightmap_jpg,
bumpURL: "",
size: [500, 300, 500],
altitude: -100,
};
return (
<scene>
<directionalLight args={[0xffffff, Math.PI]} position={[100, 20, 120]} />
<ambientLight args={[0xffffff, 0.5]} />
<background url={url} />
<Terrain
position-y={terrain.altitude}
xsize={terrain.size[0]}
ysize={terrain.size[1]}
zsize={terrain.size[2]}
map={terrain.mapURL}
heightMap={terrain.heightURL}
bumpMap={terrain.bumpURL}
/>
<Grass
position-y={terrain.altitude} // y value in world coords
numBlades={100000} // # of blades
bladeScale={0.3} // scale of blade length
width={terrain.size[0]} // width of grass terrain
hsize={terrain.size[1]} // height of grass terrain
heightMap={terrain.heightURL} // heightmap of grass terrain or its texture url
/>
</scene>
);
};
const DemoInstancedModel = () => {
const ref = useRef(null);
const dim = 2; // two-dim grids
const nlines = 10; // number of grid lines along each axis
const spacing = 20; // grid spacing along each axis
const count = Math.pow(nlines, dim);
const col = new THREE.Color();
const matrices = [],
colors = [];
for (let i = 0; i < count; i++) {
matrices.push(new THREE.Matrix4());
colors.push(new THREE.Color());
}
const posFn = (x, y, z, t) => [
x + Math.sin(x / 2),
0.3 * Math.sin(x + t) * Math.sin(z + t),
z + Math.sin(z / 2),
];
const rotFn = (x, y, z, t) => [
0.2 * Math.sin(x / 4 + t),
Math.atan2(x, z),
0,
];
const scaleFn = (x, y, z, t) => {
const s = Math.sin(x + y + t) * 0.5 + 1.5;
return [s, s, s];
};
const colorFn = (x, y, z, t) => [
Math.sin(x + t) * 0.5 + 0.5,
1,
Math.cos(z + t) * 0.5 + 0.5,
];
const transformInstance = (t = 0) => {
gridCoords(nlines, spacing, dim).forEach((p, i) => {
const trfm = transformMatrix([p[0], 0, p[1], t], posFn, rotFn, scaleFn);
matrices[i].copy(trfm);
});
return matrices;
};
const colorInstance = (t = 0) => {
gridCoords(nlines, spacing, dim).forEach((p, i) => {
col.fromArray(colorFn(p[0], 0, p[1], t));
colors[i].copy(col);
});
return colors;
};
const initTransform = () => transformInstance(0);
const updateTransform = (t) => transformInstance(t);
const initColor = () => colorInstance(0);
const updateColor = (t) => colorInstance(t);
useFrame((t) => {
if (!ref.current) return;
const instMesh = ref.current;
instMesh.rotation.y = t * 0.5;
});
return (
<InstancedModel
ref={ref}
count={count}
scale={4}
url={LeePerrySmith_glb}
animIdx={2}
initTransform={initTransform}
updateTransform={updateTransform}
initColor={initColor}
updateColor={updateColor}
/>
);
};
const DemoInstancedModel2 = () => {
const near = 0.1;
const far = 1000;
const aspect = window.innerWidth / window.innerHeight;
const width = far / 2;
const dim = 2;
const nlines = 10;
const count = Math.pow(nlines, dim);
const tmat = new THREE.Matrix4();
const smat = new THREE.Matrix4();
const matrices = [];
for (let i = 0; i < count; i++) matrices.push(new THREE.Matrix4());
const example = 2;
const initTransform = (instMeshes) => {
if (example === 1) {
const spacing = width / nlines;
gridCoords(nlines, spacing, dim).forEach((p, i) => {
tmat
.makeRotationY(Math.PI * (Math.random() * 2 - 1))
.setPosition(p[0], 0, p[1]);
smat.makeScale(3.5, 3.5, 3.5);
tmat.multiply(smat);
matrices[i].copy(tmat);
});
return matrices;
}
if (example === 2) {
for (let i = 0; i < count; i++) {
tmat.makeScale(3.5, 3.5, 3.5);
const x = Math.random() * width - width / 2;
const z = Math.random() * width - width / 2;
tmat.setPosition(x, 0.0, z);
matrices[i].copy(tmat);
}
return matrices;
}
};
const deltaTransform = (t, instMeshes) => {
if (example === 1) {
for (let i = 0; i < count; i++) {
tmat
.makeRotationY(0.01 * Math.random())
.setPosition(0, 0, 0.1 * Math.random());
matrices[i].copy(tmat);
}
return matrices;
}
if (example === 2 && instMeshes.length > 0) {
const zmax = width / 2;
for (let i = 0; i < count; i++) {
// move the front cow to the back
instMeshes[0].getMatrixAt(i, tmat);
const zpos = tmat.elements[14]; // zpos = z position of i-th instance
const dz = zpos > zmax ? -zmax / 1.7 : 0.1 * Math.random();
tmat.makeRotationY(0.01 * (Math.random() - 0.5)).setPosition(0, 0, dz);
matrices[i].copy(tmat);
}
return matrices;
}
};
const camRef = useRef(null);
const cowRef = useRef(null);
useFrame((t) => {
if (camRef.current) {
const camera = camRef.current;
const r = far / 5;
camera.position.set(
r * Math.sin(t / 5),
r / 2 + (r / 3) * Math.cos(t / 2.5),
r * Math.cos(t / 5)
);
camera.lookAt(0, 0, 0);
}
});
return (
<>
<perspectiveCamera ref={camRef} args={[60, aspect, near, far]} />
<plane
rotation-x={Math.PI / -2}
args={[width + 100, width + 100]}
type={"lambert"}
map={moss_diffuse_jpg}
/>
<SkyDome />
<Grass
width={width + 100}
bladeScale={0.4}
numBlades={50000}
position-y={-width / 200}
/>
<InstancedModel
ref={cowRef}
count={count}
url={Cow_zip}
animIdx={2}
initTransform={initTransform}
deltaTransform={deltaTransform}
isRandomColor={true}
/>
</>
);
};
const DemoExamples = ({ example }) => {
switch (example) {
case 31:
return <DemoModelLoading />;
case 32:
return <DemoModelCreation />;
case 33:
return <DemoGeometries />;
case 34:
return <DemoGeometries2 />;
case 35:
return <DemoOceanScene />;
case 36:
return <DemoShipOnTheSea />;
case 37:
return <DemoSkyTerrain />;
case 38:
return <DemoGrassScene />;
case 39:
return <DemoInstancedModel />;
case 40:
return <DemoInstancedModel2 />;
}
};
export { DemoExamples };
Last updated