JSFiddle - React, Tailwind, and code Playground

by mcoirad

JavaScript

import * as THREE from "three";

const scene = new THREE.Scene();

const metalMaterial = new THREE.MeshStandardMaterial({
  color: 0x707780,
  roughness: 0.55,
  metalness: 0.7
});

const jointMaterial = new THREE.MeshStandardMaterial({
  color: 0x22252a,
  roughness: 0.4,
  metalness: 0.9
});

function createBox(width, height, depth, material = metalMaterial) {
  const geometry = new THREE.BoxGeometry(width, height, depth);
  const mesh = new THREE.Mesh(geometry, material);

  mesh.castShadow = true;
  mesh.receiveShadow = true;

  return mesh;
}

/**
 * Creates a limb whose top begins at its parent's origin.
 * The limb extends downward along local Y.
 */
function createLimb({
  length,
  width,
  depth = width,
  material = metalMaterial
}) {
  const joint = new THREE.Group();

  const limb = createBox(width, length, depth, material);
  limb.position.y = -length / 2;

  joint.add(limb);

  return {
    joint,
    mesh: limb,
    endY: -length
  };
}

function addJointMarker(parent, radius) {
  const geometry = new THREE.SphereGeometry(radius, 12, 8);
  const marker = new THREE.Mesh(geometry, jointMaterial);

  marker.castShadow = true;
  parent.add(marker);

  return marker;
}

function createArm({
  side,
  shoulderX,
  shoulderY,
  upperArmLength,
  forearmLength,
  armWidth
}) {
  const sign = side === "left" ? -1 : 1;

  const shoulder = new THREE.Group();
  shoulder.name = `${side}Shoulder`;
  shoulder.position.set(shoulderX, shoulderY, 0);

  addJointMarker(shoulder, armWidth * 0.65);

  /*
   * Rotate the arm's local downward direction slightly outward.
   * Positive and negative Z rotations distinguish the two sides.
   */
  shoulder.rotation.z = -sign * 0.08;

  const upperArm = createLimb({
    length: upperArmLength,
    width: armWidth,
    depth: armWidth * 1.15
  });

  shoulder.add(upperArm.joint);

  const elbow = new THREE.Group();
  elbow.name =...