Spaces:
Running on Zero
Running on Zero
Deploy Karate Wiener (kimodo kata maker)
Browse files- .gitattributes +1 -0
- app.py +36 -1
- assets/citizen_skin_color.png +3 -0
.gitattributes
CHANGED
|
@@ -97,3 +97,4 @@ kata_sequence_taikyoku.png filter=lfs diff=lfs merge=lfs -text
|
|
| 97 |
kick_stances_verify.png filter=lfs diff=lfs merge=lfs -text
|
| 98 |
stance_library_verify.png filter=lfs diff=lfs merge=lfs -text
|
| 99 |
assets/default_scene.webp filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 97 |
kick_stances_verify.png filter=lfs diff=lfs merge=lfs -text
|
| 98 |
stance_library_verify.png filter=lfs diff=lfs merge=lfs -text
|
| 99 |
assets/default_scene.webp filter=lfs diff=lfs merge=lfs -text
|
| 100 |
+
assets/citizen_skin_color.png filter=lfs diff=lfs merge=lfs -text
|
app.py
CHANGED
|
@@ -1013,7 +1013,17 @@ def _render_preview_html(
|
|
| 1013 |
payload_json = json.dumps(payload, separators=(",", ":")).replace("</", "<\\/")
|
| 1014 |
# Display-character catalog (GLB + mapping) is injected per-render, never
|
| 1015 |
# stored in preview records. Only offer characters whose GLB actually loaded.
|
| 1016 |
-
catalog = [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1017 |
char_json = json.dumps(catalog, separators=(",", ":")).replace("</", "<\\/")
|
| 1018 |
cfg = {
|
| 1019 |
"clothing": CLOTHING_CATALOG,
|
|
@@ -1985,6 +1995,30 @@ def _render_preview_html(
|
|
| 1985 |
let character = null; // {{ root, anim }}
|
| 1986 |
let mode = 'skeleton';
|
| 1987 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1988 |
async function buildCharacter(entry) {{
|
| 1989 |
const loader = new GLTFLoader();
|
| 1990 |
const gltf = await loader.parseAsync(decodeBytes(entry.glb_b64).buffer, '');
|
|
@@ -1993,6 +2027,7 @@ def _render_preview_html(
|
|
| 1993 |
let skinned = null;
|
| 1994 |
root.traverse(o => {{ if (o.isSkinnedMesh) {{ if (!skinned) skinned = o; o.frustumCulled = false; }} }});
|
| 1995 |
if (!skinned) throw new Error('GLB has no skinned mesh');
|
|
|
|
| 1996 |
root.visible = false;
|
| 1997 |
scene.add(root);
|
| 1998 |
root.updateMatrixWorld(true);
|
|
|
|
| 1013 |
payload_json = json.dumps(payload, separators=(",", ":")).replace("</", "<\\/")
|
| 1014 |
# Display-character catalog (GLB + mapping) is injected per-render, never
|
| 1015 |
# stored in preview records. Only offer characters whose GLB actually loaded.
|
| 1016 |
+
catalog = []
|
| 1017 |
+
for c in _CHARACTER_CATALOG:
|
| 1018 |
+
if not (c.get("id") == "skeleton" or c.get("glb_b64")):
|
| 1019 |
+
continue
|
| 1020 |
+
c = dict(c)
|
| 1021 |
+
# The s&box citizen GLB ships untextured (its UV atlas path doesn't resolve);
|
| 1022 |
+
# supply the decompiled skin atlas (served from the dataset like clothing) so
|
| 1023 |
+
# the viewer can map it onto the body. UVs are preserved through UniRig.
|
| 1024 |
+
if c.get("id") == "citizen":
|
| 1025 |
+
c["texture"] = f"{_VIEWER_ASSET_BASE}/textures/citizen_skin_color.png"
|
| 1026 |
+
catalog.append(c)
|
| 1027 |
char_json = json.dumps(catalog, separators=(",", ":")).replace("</", "<\\/")
|
| 1028 |
cfg = {
|
| 1029 |
"clothing": CLOTHING_CATALOG,
|
|
|
|
| 1995 |
let character = null; // {{ root, anim }}
|
| 1996 |
let mode = 'skeleton';
|
| 1997 |
|
| 1998 |
+
// Map a skin atlas (e.g. the decompiled s&box citizen skin) onto a character's
|
| 1999 |
+
// body materials. One shared UV atlas covers the whole body; skip eye/mouth
|
| 2000 |
+
// sub-materials (their own UVs). flipY=false = glTF UV convention.
|
| 2001 |
+
const _charTexLoader = new THREE.TextureLoader();
|
| 2002 |
+
_charTexLoader.setCrossOrigin('anonymous');
|
| 2003 |
+
function applyCharSkin(root, url) {{
|
| 2004 |
+
const tex = _charTexLoader.load(url);
|
| 2005 |
+
tex.colorSpace = THREE.SRGBColorSpace;
|
| 2006 |
+
tex.flipY = false;
|
| 2007 |
+
tex.anisotropy = renderer.capabilities.getMaxAnisotropy();
|
| 2008 |
+
root.traverse(o => {{
|
| 2009 |
+
if (!o.isMesh && !o.isSkinnedMesh) return;
|
| 2010 |
+
const mats = Array.isArray(o.material) ? o.material : [o.material];
|
| 2011 |
+
for (const m of mats) {{
|
| 2012 |
+
if (!m) continue;
|
| 2013 |
+
const name = (m.name || o.name || '').toLowerCase();
|
| 2014 |
+
if (/eye|mouth|teeth|tongue|brow|lash|cornea/.test(name)) continue;
|
| 2015 |
+
m.map = tex;
|
| 2016 |
+
if (m.color) m.color.setScalar(1); // don't tint the atlas with a leftover base color
|
| 2017 |
+
m.needsUpdate = true;
|
| 2018 |
+
}}
|
| 2019 |
+
}});
|
| 2020 |
+
}}
|
| 2021 |
+
|
| 2022 |
async function buildCharacter(entry) {{
|
| 2023 |
const loader = new GLTFLoader();
|
| 2024 |
const gltf = await loader.parseAsync(decodeBytes(entry.glb_b64).buffer, '');
|
|
|
|
| 2027 |
let skinned = null;
|
| 2028 |
root.traverse(o => {{ if (o.isSkinnedMesh) {{ if (!skinned) skinned = o; o.frustumCulled = false; }} }});
|
| 2029 |
if (!skinned) throw new Error('GLB has no skinned mesh');
|
| 2030 |
+
if (entry.texture) applyCharSkin(root, entry.texture); // map the s&box skin atlas onto the body
|
| 2031 |
root.visible = false;
|
| 2032 |
scene.add(root);
|
| 2033 |
root.updateMatrixWorld(true);
|
assets/citizen_skin_color.png
ADDED
|
Git LFS Details
|