diff --git a/public/loot-assets b/public/loot-assets
new file mode 160000
index 000000000..24e2d4701
--- /dev/null
+++ b/public/loot-assets
@@ -0,0 +1 @@
+Subproject commit 24e2d470149e6a3235cb0cce9135bea4abafb1a3
diff --git a/src/components/BoneSelector.css b/src/components/BoneSelector.css
new file mode 100644
index 000000000..db0eb6144
--- /dev/null
+++ b/src/components/BoneSelector.css
@@ -0,0 +1,51 @@
+.bone-selector {
+ position: absolute;
+ display: flex;
+ justify-content: center; /* Center horizontally */
+ align-items: center; /* Center vertically */
+ height: 100vh; /* Full viewport height */
+ width: 100vw; /* Full viewport height */
+ pointer-events: none;
+ top:-30px;
+}
+
+.character-base {
+ /* position: relative;
+ display: inline-block;
+ width: 300px;
+ height: 600px;
+ display: flex;
+ justify-content: center;
+ text-align: center; */
+ user-select: none;
+
+ height: 70%;
+ position: relative;
+ display: inline-block;
+}
+
+.character-base img {
+ height: 100%;
+ /* width: 100%; */
+}
+
+.bone-dot {
+ pointer-events: auto;
+ user-select: none;
+ position: relative;
+ width: 0px;
+ height: 0px;
+ background-size: cover;
+
+ cursor: pointer;
+}
+
+.bone-dot img {
+ width: 30px;
+ height: 30px;
+ transform: translate(-50%, -50%); /* Center the dot */
+}
+
+.bone-dot-position {
+ position: absolute;
+}
\ No newline at end of file
diff --git a/src/components/BoneSelector.jsx b/src/components/BoneSelector.jsx
new file mode 100644
index 000000000..197d0a4d3
--- /dev/null
+++ b/src/components/BoneSelector.jsx
@@ -0,0 +1,83 @@
+import React, { useState } from 'react';
+import './BoneSelector.css'; // Import CSS for styling
+import boneDot from "../images/humanoid_option.png"
+import humanoidUI from "../images/humanoid_ui.png"
+
+// Example CharacterBase component
+const CharacterBase = ({}) => {
+ return (
+
+
Trait Position
+
+
+ { const v = Number(e.target.value); applyTranslateDelta(v-pos.x,0,0); setPos(prev=>({...prev,x:v})) }} />
+ { const v=Number(e.target.value); applyTranslateDelta(v-pos.x,0,0); setPos(prev=>({...prev,x:v})) }} />
+
+
+
+ { const v = Number(e.target.value); applyTranslateDelta(0,v-pos.y,0); setPos(prev=>({...prev,y:v})) }} />
+ { const v=Number(e.target.value); applyTranslateDelta(0,v-pos.y,0); setPos(prev=>({...prev,y:v})) }} />
+
+
+
+ { const v = Number(e.target.value); applyTranslateDelta(0,0,v-pos.z); setPos(prev=>({...prev,z:v})) }} />
+ { const v=Number(e.target.value); applyTranslateDelta(0,0,v-pos.z); setPos(prev=>({...prev,z:v})) }} />
+
+
+
Trait Rotation
+
+
+ { const v=Number(e.target.value); applyRotateDelta(v-rot.x,0,0); setRot(prev=>({...prev,x:v})) }} />
+ { const v=Number(e.target.value); applyRotateDelta(v-rot.x,0,0); setRot(prev=>({...prev,x:v})) }} />
+
+
+
+ { const v=Number(e.target.value); applyRotateDelta(0,v-rot.y,0); setRot(prev=>({...prev,y:v})) }} />
+ { const v=Number(e.target.value); applyRotateDelta(0,v-rot.y,0); setRot(prev=>({...prev,y:v})) }} />
+
+
+
+ { const v=Number(e.target.value); applyRotateDelta(0,0,v-rot.z); setRot(prev=>({...prev,z:v})) }} />
+ { const v=Number(e.target.value); applyRotateDelta(0,0,v-rot.z); setRot(prev=>(({...prev,z:v})) ) }} />
+
+
+
Trait Scale
+
+
+ { const v=Number(e.target.value); const dx=v-scl.x; const dy=v-scl.y; const dz=v-scl.z; applyScaleDelta(dx,dy,dz); setScl({x:v,y:v,z:v}); setU(v); }} />
+ { const v=Number(e.target.value); const dx=v-scl.x; const dy=v-scl.y; const dz=v-scl.z; applyScaleDelta(dx,dy,dz); setScl({x:v,y:v,z:v}); setU(v); }} />
+
+
+
+ { const v=Number(e.target.value); applyScaleDelta(v-scl.x,0,0); setScl(prev=>({...prev,x:v})); setU((v+scl.y+scl.z)/3); }} />
+ { const v=Number(e.target.value); applyScaleDelta(v-scl.x,0,0); setScl(prev=>({...prev,x:v})); setU((v+scl.y+scl.z)/3); }} />
+
+
+
+ { const v=Number(e.target.value); applyScaleDelta(0,v-scl.y,0); setScl(prev=>({...prev,y:v})); setU((scl.x+v+scl.z)/3); }} />
+ { const v=Number(e.target.value); applyScaleDelta(0,v-scl.y,0); setScl(prev=>({...prev,y:v})); setU((scl.x+v+scl.z)/3); }} />
+
+
+
+ { const v=Number(e.target.value); applyScaleDelta(0,0,v-scl.z); setScl(prev=>({...prev,z:v})); setU((scl.x+scl.y+v)/3); }} />
+ { const v=Number(e.target.value); applyScaleDelta(0,0,v-scl.z); setScl(prev=>({...prev,z:v})); setU((scl.x+scl.y+v)/3); }} />
+
+
+
Attach To Bone
+
+
+
+
+
+
Utilities
+
+
+
+
+
+
+ )
+}
+
+
diff --git a/src/components/TransformInspector.module.css b/src/components/TransformInspector.module.css
new file mode 100644
index 000000000..3a427109a
--- /dev/null
+++ b/src/components/TransformInspector.module.css
@@ -0,0 +1,42 @@
+.panel{
+ position: absolute;
+ top: 80px;
+ right: 24px;
+ width: 260px;
+ padding: 12px;
+ border-radius: 12px;
+ background: rgba(5,11,14,0.8);
+ color: #fff;
+ backdrop-filter: blur(22.5px);
+ z-index: 1001;
+}
+.header{
+ font-weight: 800;
+ letter-spacing: 1px;
+ margin: 8px 0;
+}
+.row{
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin: 8px 0;
+}
+label{ width: 96px; font-size: 12px; color: #ddd; }
+.select{ flex:1; }
+.num{
+ width: 48px;
+ padding: 6px 8px;
+ border-radius: 6px;
+ border: 1px solid rgba(255,255,255,0.14);
+ background: rgba(255,255,255,0.06);
+ color: #fff;
+}
+.btn{
+ padding: 6px 10px;
+ border: 1px solid rgba(255,255,255,0.14);
+ background: rgba(255,255,255,0.06);
+ color: #fff;
+ border-radius: 8px;
+}
+
+
diff --git a/src/components/TransformToolbar.jsx b/src/components/TransformToolbar.jsx
new file mode 100644
index 000000000..a07b0deac
--- /dev/null
+++ b/src/components/TransformToolbar.jsx
@@ -0,0 +1,94 @@
+import React from 'react'
+import styles from './TransformToolbar.module.css'
+import { SceneContext } from '../context/SceneContext'
+
+export default function TransformToolbar(){
+ const {
+ transformMode,
+ setTransformMode,
+ transformSnap,
+ setTransformSnap,
+ transformTarget,
+ detachTransformTarget,
+ applyTranslateDelta,
+ applyRotateDelta,
+ applyScaleDelta,
+ } = React.useContext(SceneContext)
+
+ const [plane, setPlane] = React.useState('XY') // for translate only
+
+ const onSnapChange = (key) => (e) => {
+ const v = Number(e.target.value)
+ setTransformSnap({ ...transformSnap, [key]: isNaN(v) ? 0 : v })
+ }
+
+ if (!transformTarget) return null
+
+ const stepValue = transformMode === 'translate' ? transformSnap.t : transformMode === 'rotate' ? transformSnap.r : transformSnap.s
+ const setStepValue = (v) => {
+ const num = Number(v)
+ if (isNaN(num)) return
+ if (transformMode === 'translate') setTransformSnap({ ...transformSnap, t: num })
+ else if (transformMode === 'rotate') setTransformSnap({ ...transformSnap, r: num })
+ else setTransformSnap({ ...transformSnap, s: num })
+ }
+
+ const onArrow = (dir) => () => {
+ if (transformMode === 'translate'){
+ const s = transformSnap.t
+ if (plane === 'XY'){
+ if (dir==='up') return applyTranslateDelta(0, s, 0)
+ if (dir==='down') return applyTranslateDelta(0, -s, 0)
+ } else {
+ if (dir==='up') return applyTranslateDelta(0, 0, s)
+ if (dir==='down') return applyTranslateDelta(0, 0, -s)
+ }
+ if (dir==='left') return applyTranslateDelta(-s, 0, 0)
+ if (dir==='right') return applyTranslateDelta(s, 0, 0)
+ }
+ if (transformMode === 'rotate'){
+ const r = transformSnap.r
+ if (dir==='up') return applyRotateDelta(r, 0, 0)
+ if (dir==='down') return applyRotateDelta(-r, 0, 0)
+ if (dir==='left') return applyRotateDelta(0, -r, 0)
+ if (dir==='right') return applyRotateDelta(0, r, 0)
+ }
+ if (transformMode === 'scale'){
+ const s = transformSnap.s
+ const d = (dir==='up' || dir==='right') ? s : -s
+ return applyScaleDelta(d, d, d)
+ }
+ }
+
+ return (
+