Compare commits
4 Commits
d46b5ca3d6
...
d4e86e60ff
| Author | SHA1 | Date | |
|---|---|---|---|
| d4e86e60ff | |||
| 8bcefd93bc | |||
| eaee23df40 | |||
| 2ec78b5d74 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -22,3 +22,5 @@ dist-ssr
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
public/*.json
|
||||
src/assets/*.json
|
||||
|
||||
198
src/App.tsx
198
src/App.tsx
@ -5,6 +5,10 @@ import { FontLoader } from 'three/addons/loaders/FontLoader.js'
|
||||
import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'
|
||||
import HelvetikerRegular from "three/examples/fonts/helvetiker_regular.typeface.json"
|
||||
import { useEffect, useRef, useState, JSX } from 'react'
|
||||
import POSE_3D_ from "./assets/result_ae_01_ae_08.json"
|
||||
|
||||
// F, 133, 3
|
||||
const POSE_3D: Array<Array<[number, number, number]>> = POSE_3D_ as [number, number, number][][]
|
||||
|
||||
const THREE_ADDONS = {
|
||||
FontLoader,
|
||||
@ -30,6 +34,59 @@ const Z_UP_TO_Y_UP = new THREE.Matrix4().set(
|
||||
0, 0, 0, 1
|
||||
)
|
||||
|
||||
const Z_UP_TO_Y_UP_PRIME = new THREE.Matrix4().set(
|
||||
-1, 0, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, -1, 0, 0,
|
||||
0, 0, 0, 1
|
||||
)
|
||||
|
||||
// Color definitions for different body parts
|
||||
const COLOR_SPINE = new THREE.Color(138 / 255, 201 / 255, 38 / 255) // green, spine & head
|
||||
const COLOR_ARMS = new THREE.Color(255 / 255, 202 / 255, 58 / 255) // yellow, arms & shoulders
|
||||
const COLOR_LEGS = new THREE.Color(25 / 255, 130 / 255, 196 / 255) // blue, legs & hips
|
||||
const COLOR_FINGERS = new THREE.Color(255 / 255, 0, 0) // red, fingers
|
||||
const COLOR_FACE = new THREE.Color(255 / 255, 200 / 255, 0) // yellow, face
|
||||
const COLOR_FOOT = new THREE.Color(255 / 255, 128 / 255, 0) // orange, foot
|
||||
const COLOR_HEAD = new THREE.Color(255 / 255, 0, 255 / 255) // purple, head
|
||||
|
||||
// Body bone connections
|
||||
const BODY_BONES = [
|
||||
// legs
|
||||
[15, 13], [13, 11], [16, 14], [14, 12], [11, 12], // legs
|
||||
[5, 11], [6, 12], [5, 6], // torso
|
||||
[5, 7], [7, 9], [6, 8], [8, 10], // arms
|
||||
[1, 2], [0, 1], [0, 2], [1, 3], [2, 4], // head
|
||||
[15, 17], [15, 18], [15, 19], // left foot
|
||||
[16, 20], [16, 21], [16, 22], // right foot
|
||||
]
|
||||
|
||||
// Body bone colors
|
||||
const BODY_BONE_COLORS = [
|
||||
COLOR_LEGS, COLOR_LEGS, COLOR_LEGS, COLOR_LEGS, COLOR_LEGS,
|
||||
COLOR_SPINE, COLOR_SPINE, COLOR_SPINE,
|
||||
COLOR_ARMS, COLOR_ARMS, COLOR_ARMS, COLOR_ARMS,
|
||||
COLOR_HEAD, COLOR_HEAD, COLOR_HEAD, COLOR_HEAD, COLOR_HEAD,
|
||||
COLOR_FOOT, COLOR_FOOT, COLOR_FOOT,
|
||||
COLOR_FOOT, COLOR_FOOT, COLOR_FOOT,
|
||||
]
|
||||
|
||||
// Hand bone connections (in pairs of [start, end] indices)
|
||||
const HAND_BONES = [
|
||||
// right hand
|
||||
[91, 92], [92, 93], [93, 94], [94, 95], // right thumb
|
||||
[91, 96], [96, 97], [97, 98], [98, 99], // right index
|
||||
[91, 100], [100, 101], [101, 102], [102, 103], // right middle
|
||||
[91, 104], [104, 105], [105, 106], [106, 107], // right ring
|
||||
[91, 108], [108, 109], [109, 110], [110, 111], // right pinky
|
||||
// left hand
|
||||
[112, 113], [113, 114], [114, 115], [115, 116], // left thumb
|
||||
[112, 117], [117, 118], [118, 119], [119, 120], // left index
|
||||
[112, 121], [121, 122], [122, 123], [123, 124], // left middle
|
||||
[112, 125], [125, 126], [126, 127], [127, 128], // left ring
|
||||
[112, 129], [129, 130], [130, 131], [131, 132] // left pinky
|
||||
]
|
||||
|
||||
const DEFAULT_TRANSFORMATION_MATRIX = [
|
||||
1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
@ -198,6 +255,145 @@ const Scene = () => {
|
||||
return final
|
||||
}
|
||||
|
||||
interface Human3DSkeletonProps {
|
||||
startFrame?: number
|
||||
jointRadius?: number
|
||||
boneRadius?: number
|
||||
showJoints?: boolean
|
||||
showBones?: boolean
|
||||
frameRate?: number
|
||||
}
|
||||
|
||||
const Human3DSkeleton = ({
|
||||
startFrame = 0,
|
||||
jointRadius = 0.01,
|
||||
boneRadius = 0.005,
|
||||
showJoints = true,
|
||||
showBones = true,
|
||||
frameRate = 30
|
||||
}: Human3DSkeletonProps) => {
|
||||
const [frameIndex, setFrameIndex] = useState(startFrame)
|
||||
const totalFrames = POSE_3D.length
|
||||
|
||||
// Use frame to animate through the skeleton poses
|
||||
useFrame((state, delta) => {
|
||||
// Calculate next frame based on desired frame rate and delta time
|
||||
setFrameIndex(prevFrame => {
|
||||
// Calculate next frame
|
||||
const nextFrame = prevFrame + frameRate * delta
|
||||
// Loop back to start if we reach the end
|
||||
return nextFrame >= totalFrames ? 0 : nextFrame
|
||||
})
|
||||
})
|
||||
|
||||
// Get the current frame joints - use Math.floor to get the nearest frame
|
||||
const currentFrame = Math.floor(frameIndex) % totalFrames
|
||||
const joints = POSE_3D[currentFrame]
|
||||
|
||||
// Function to get appropriate color for a joint index
|
||||
const getJointColor = (idx: number) => {
|
||||
// Face joints (23-90)
|
||||
if (idx >= 23 && idx <= 90) return COLOR_FACE
|
||||
// Hand joints (91-132)
|
||||
if (idx >= 91 && idx <= 132) return COLOR_FINGERS
|
||||
// Foot joints (17-22)
|
||||
if (idx >= 17 && idx <= 22) return COLOR_FOOT
|
||||
// Head (0-4)
|
||||
if (idx <= 4) return COLOR_HEAD
|
||||
// Arms (5-10)
|
||||
if (idx >= 5 && idx <= 10) return COLOR_ARMS
|
||||
// Legs (11-16)
|
||||
if (idx >= 11 && idx <= 16) return COLOR_LEGS
|
||||
// Default
|
||||
return COLOR_SPINE
|
||||
}
|
||||
|
||||
// Transform a joint position using the coordinate system conversion
|
||||
const transformJointPosition = (j: [number, number, number]) => {
|
||||
const [x, y, z] = j
|
||||
const V = new THREE.Vector3(x, y, z)
|
||||
const worldCvt = Z_UP_TO_Y_UP_PRIME.clone()
|
||||
V.applyMatrix4(worldCvt)
|
||||
return V
|
||||
}
|
||||
|
||||
// Create the joint spheres
|
||||
const jointMeshes = showJoints ? joints.map((j, idx) => {
|
||||
const position = transformJointPosition(j)
|
||||
const color = getJointColor(idx)
|
||||
|
||||
return (
|
||||
<mesh key={`joint-${idx}`} position={position}>
|
||||
<sphereGeometry args={[jointRadius, 16, 16]} />
|
||||
<meshStandardMaterial color={color} />
|
||||
</mesh>
|
||||
)
|
||||
}) : null
|
||||
|
||||
// Create the bone cylinders
|
||||
const boneMeshes = showBones ? (
|
||||
<>
|
||||
{BODY_BONES.map((bone, idx) => {
|
||||
const [startIdx, endIdx] = bone
|
||||
if (startIdx >= joints.length || endIdx >= joints.length) return null
|
||||
|
||||
const startPos = transformJointPosition(joints[startIdx])
|
||||
const endPos = transformJointPosition(joints[endIdx])
|
||||
const color = BODY_BONE_COLORS[idx]
|
||||
|
||||
// Calculate midpoint and length
|
||||
const midpoint = new THREE.Vector3().addVectors(startPos, endPos).multiplyScalar(0.5)
|
||||
const length = startPos.distanceTo(endPos)
|
||||
|
||||
// Calculate rotation
|
||||
const direction = new THREE.Vector3().subVectors(endPos, startPos).normalize()
|
||||
const quaternion = new THREE.Quaternion()
|
||||
const up = new THREE.Vector3(0, 1, 0)
|
||||
quaternion.setFromUnitVectors(up, direction)
|
||||
|
||||
return (
|
||||
<mesh key={`bone-body-${idx}`} position={midpoint} quaternion={quaternion}>
|
||||
<cylinderGeometry args={[boneRadius, boneRadius, length, 8]} />
|
||||
<meshStandardMaterial color={color} />
|
||||
</mesh>
|
||||
)
|
||||
})}
|
||||
|
||||
{HAND_BONES.map((bone, idx) => {
|
||||
const [startIdx, endIdx] = bone
|
||||
if (startIdx >= joints.length || endIdx >= joints.length) return null
|
||||
|
||||
const startPos = transformJointPosition(joints[startIdx])
|
||||
const endPos = transformJointPosition(joints[endIdx])
|
||||
|
||||
// Calculate midpoint and length
|
||||
const midpoint = new THREE.Vector3().addVectors(startPos, endPos).multiplyScalar(0.5)
|
||||
const length = startPos.distanceTo(endPos)
|
||||
|
||||
// Calculate rotation
|
||||
const direction = new THREE.Vector3().subVectors(endPos, startPos).normalize()
|
||||
const quaternion = new THREE.Quaternion()
|
||||
const up = new THREE.Vector3(0, 1, 0)
|
||||
quaternion.setFromUnitVectors(up, direction)
|
||||
|
||||
return (
|
||||
<mesh key={`bone-hand-${idx}`} position={midpoint} quaternion={quaternion}>
|
||||
<cylinderGeometry args={[boneRadius, boneRadius, length, 8]} />
|
||||
<meshStandardMaterial color={COLOR_FINGERS} />
|
||||
</mesh>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
) : null
|
||||
|
||||
return (
|
||||
<group>
|
||||
{jointMeshes}
|
||||
{boneMeshes}
|
||||
</group>
|
||||
)
|
||||
}
|
||||
|
||||
const scene = (<group>
|
||||
{/* <OrbitControls /> */}
|
||||
<ambientLight intensity={0.05} />
|
||||
@ -211,13 +407,13 @@ const Scene = () => {
|
||||
return <CameraViewFromExtrinsic key={key} name={`${key}(${fov_x.toFixed(1)})`} extrinsic={preProcessExtrinsic(value)} fov={fov_x} aspect={IMAGE_WIDTH / IMAGE_HEIGHT} far={far} />
|
||||
})}
|
||||
<Axes />
|
||||
<Human3DSkeleton jointRadius={0.005} boneRadius={0.0025} frameRate={24} />
|
||||
</group>)
|
||||
return (
|
||||
// Note that we don't need to import anything, All three.js objects will be treated
|
||||
// as native JSX elements, just like you can just write <div /> or <span /> in
|
||||
// regular ReactDOM. The general rule is that Fiber components are available under
|
||||
// the camel-case version of their name in three.js.
|
||||
|
||||
<>
|
||||
<CameraControls />
|
||||
<Stats />
|
||||
|
||||
Reference in New Issue
Block a user