@mml-io/3d-web-client-core 0.0.0-experimental-1f6e4fb-20250728 → 0.0.0-experimental-7b195ca-20250729
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
|
|
1
|
-
import { Color,
|
1
|
+
import { Color, Object3D } from "three";
|
2
2
|
import { ColorPartName } from "../CharacterModel";
|
3
3
|
import { InstancedMesh2 } from "./vendor/";
|
4
4
|
export type ColorSamplingOptions = {
|
@@ -9,6 +9,6 @@ export type ColorSamplingOptions = {
|
|
9
9
|
};
|
10
10
|
debug?: boolean;
|
11
11
|
};
|
12
|
-
export declare function captureCharacterColors(characterMesh:
|
12
|
+
export declare function captureCharacterColors(characterMesh: Object3D, options: ColorSamplingOptions): Map<ColorPartName, Color>;
|
13
13
|
export declare function captureCharacterColorsFromObject3D(object3D: Object3D, options: ColorSamplingOptions): Map<ColorPartName, Color>;
|
14
14
|
export declare function updateDebugTextureCanvas(instancedMesh: InstancedMesh2): void;
|
package/build/index.js
CHANGED
@@ -1407,7 +1407,7 @@ import {
|
|
1407
1407
|
Group,
|
1408
1408
|
LoopRepeat,
|
1409
1409
|
Mesh,
|
1410
|
-
SkinnedMesh as
|
1410
|
+
SkinnedMesh as SkinnedMesh2,
|
1411
1411
|
Vector3 as Vector33
|
1412
1412
|
} from "three";
|
1413
1413
|
|
@@ -1721,155 +1721,13 @@ import {
|
|
1721
1721
|
Color as Color2,
|
1722
1722
|
OrthographicCamera,
|
1723
1723
|
Scene,
|
1724
|
-
SkinnedMesh
|
1724
|
+
SkinnedMesh,
|
1725
1725
|
Vector2,
|
1726
1726
|
Vector3 as Vector32,
|
1727
1727
|
WebGLRenderer,
|
1728
1728
|
WebGLRenderTarget
|
1729
1729
|
} from "three";
|
1730
1730
|
import * as SkeletonUtils from "three/examples/jsm/utils/SkeletonUtils.js";
|
1731
|
-
|
1732
|
-
// src/character/instancing/CharacterInstancingUtils.ts
|
1733
|
-
import { BufferAttribute, SkinnedMesh } from "three";
|
1734
|
-
import * as BufferGeometryUtils from "three/examples/jsm/utils/BufferGeometryUtils.js";
|
1735
|
-
function mergeSkinnedMeshes(skinnedMeshes, debug) {
|
1736
|
-
const geometries = [];
|
1737
|
-
const materials = [];
|
1738
|
-
const skeleton = skinnedMeshes[0].skeleton;
|
1739
|
-
for (const skinnedMesh of skinnedMeshes) {
|
1740
|
-
const geometry = skinnedMesh.geometry.clone();
|
1741
|
-
const materialIndex = materials.length;
|
1742
|
-
if (Array.isArray(skinnedMesh.material)) {
|
1743
|
-
materials.push(...skinnedMesh.material);
|
1744
|
-
for (let i = 0; i < skinnedMesh.material.length; i++) {
|
1745
|
-
geometry.addGroup(
|
1746
|
-
0,
|
1747
|
-
geometry.index ? geometry.index.count : geometry.attributes.position.count,
|
1748
|
-
materialIndex + i
|
1749
|
-
);
|
1750
|
-
}
|
1751
|
-
} else {
|
1752
|
-
materials.push(skinnedMesh.material);
|
1753
|
-
geometry.addGroup(
|
1754
|
-
0,
|
1755
|
-
geometry.index ? geometry.index.count : geometry.attributes.position.count,
|
1756
|
-
materialIndex
|
1757
|
-
);
|
1758
|
-
}
|
1759
|
-
geometries.push(geometry);
|
1760
|
-
}
|
1761
|
-
const mergedGeometry = BufferGeometryUtils.mergeGeometries(geometries, true);
|
1762
|
-
if (!mergedGeometry) {
|
1763
|
-
throw new Error("Failed to merge geometries");
|
1764
|
-
}
|
1765
|
-
const mergedMesh = new SkinnedMesh(mergedGeometry, materials);
|
1766
|
-
mergedMesh.skeleton = skeleton;
|
1767
|
-
mergedMesh.bindMatrix.copy(skinnedMeshes[0].bindMatrix);
|
1768
|
-
mergedMesh.bindMatrixInverse.copy(skinnedMeshes[0].bindMatrixInverse);
|
1769
|
-
mergedMesh.bind(skeleton, mergedMesh.bindMatrix);
|
1770
|
-
if (debug) {
|
1771
|
-
console.log(`Merged into single mesh with ${materials.length} materials`);
|
1772
|
-
}
|
1773
|
-
addVertexColorsToGeometry(mergedGeometry, materials, debug);
|
1774
|
-
return mergedMesh;
|
1775
|
-
}
|
1776
|
-
function validateAndCleanSkeleton(skinnedMesh) {
|
1777
|
-
const skeleton = skinnedMesh.skeleton;
|
1778
|
-
const nullBoneIndices = [];
|
1779
|
-
for (let i = 0; i < skeleton.bones.length; i++) {
|
1780
|
-
if (!skeleton.bones[i]) {
|
1781
|
-
nullBoneIndices.push(i);
|
1782
|
-
}
|
1783
|
-
}
|
1784
|
-
if (nullBoneIndices.length > 0) {
|
1785
|
-
skeleton.bones = skeleton.bones.filter((bone) => bone !== null && bone !== void 0);
|
1786
|
-
skeleton.update();
|
1787
|
-
}
|
1788
|
-
}
|
1789
|
-
function addVertexColorsToGeometry(geometry, materials, debug) {
|
1790
|
-
const positionAttribute = geometry.getAttribute("position");
|
1791
|
-
if (!positionAttribute) {
|
1792
|
-
console.error("No position attribute found in geometry");
|
1793
|
-
return;
|
1794
|
-
}
|
1795
|
-
const vertexCount = positionAttribute.count;
|
1796
|
-
if (debug) {
|
1797
|
-
console.log(`Geometry has ${vertexCount} vertices`);
|
1798
|
-
}
|
1799
|
-
const colors = new Float32Array(vertexCount * 3);
|
1800
|
-
for (let i = 0; i < vertexCount; i++) {
|
1801
|
-
colors[i * 3] = 1;
|
1802
|
-
colors[i * 3 + 1] = 1;
|
1803
|
-
colors[i * 3 + 2] = 1;
|
1804
|
-
}
|
1805
|
-
const materialColorCodes = {
|
1806
|
-
// bin material IDs to be replaced by the final colors on the GPU
|
1807
|
-
hair: [0, 0, 0],
|
1808
|
-
shirt_short: [0, 0, 1],
|
1809
|
-
shirt_long: [0, 1, 0],
|
1810
|
-
pants_short: [0, 1, 1],
|
1811
|
-
pants_long: [1, 0, 0],
|
1812
|
-
shoes: [1, 0, 1],
|
1813
|
-
skin: [1, 1, 0],
|
1814
|
-
lips: [1, 1, 1],
|
1815
|
-
eyes_black: [0.5, 0, 0],
|
1816
|
-
eyes_white: [0, 0.5, 0]
|
1817
|
-
};
|
1818
|
-
if (debug) {
|
1819
|
-
console.log("Geometry groups:", geometry.groups);
|
1820
|
-
console.log(
|
1821
|
-
"Materials:",
|
1822
|
-
materials.map((m) => m.name)
|
1823
|
-
);
|
1824
|
-
}
|
1825
|
-
if (geometry.groups && geometry.groups.length > 0) {
|
1826
|
-
geometry.groups.forEach((group, groupIndex) => {
|
1827
|
-
const material = materials[group.materialIndex || groupIndex];
|
1828
|
-
const materialName = (material == null ? void 0 : material.name) || `material_${groupIndex}`;
|
1829
|
-
if (debug) {
|
1830
|
-
console.log(
|
1831
|
-
`Processing group ${groupIndex}: material "${materialName}", start: ${group.start}, count: ${group.count}`
|
1832
|
-
);
|
1833
|
-
}
|
1834
|
-
const materialColor = materialColorCodes[materialName] || [
|
1835
|
-
1,
|
1836
|
-
1,
|
1837
|
-
1
|
1838
|
-
];
|
1839
|
-
if (debug) {
|
1840
|
-
console.log(
|
1841
|
-
`Using ID color [${materialColor.join(", ")}] for material "${materialName}" (${materialColor[0] === 1 && materialColor[1] === 1 && materialColor[2] === 0 ? "YELLOW=SKIN" : materialColor[0] === 0 && materialColor[1] === 0 && materialColor[2] === 1 ? "BLUE=SHIRT" : "OTHER"})`
|
1842
|
-
);
|
1843
|
-
}
|
1844
|
-
const indexAttribute = geometry.getIndex();
|
1845
|
-
if (indexAttribute) {
|
1846
|
-
for (let i = group.start; i < group.start + group.count; i++) {
|
1847
|
-
const vertexIndex = indexAttribute.getX(i);
|
1848
|
-
colors[vertexIndex * 3] = materialColor[0];
|
1849
|
-
colors[vertexIndex * 3 + 1] = materialColor[1];
|
1850
|
-
colors[vertexIndex * 3 + 2] = materialColor[2];
|
1851
|
-
}
|
1852
|
-
} else {
|
1853
|
-
const startVertex = group.start / 3;
|
1854
|
-
const vertexCount2 = group.count / 3;
|
1855
|
-
for (let i = 0; i < vertexCount2; i++) {
|
1856
|
-
const vertexIndex = startVertex + i;
|
1857
|
-
colors[vertexIndex * 3] = materialColor[0];
|
1858
|
-
colors[vertexIndex * 3 + 1] = materialColor[1];
|
1859
|
-
colors[vertexIndex * 3 + 2] = materialColor[2];
|
1860
|
-
}
|
1861
|
-
}
|
1862
|
-
});
|
1863
|
-
} else {
|
1864
|
-
console.warn("No geometry groups found, using single material coloring");
|
1865
|
-
}
|
1866
|
-
geometry.setAttribute("color", new BufferAttribute(colors, 3));
|
1867
|
-
if (debug) {
|
1868
|
-
console.log(`Added per-material vertex colors to ${vertexCount} vertices`);
|
1869
|
-
}
|
1870
|
-
}
|
1871
|
-
|
1872
|
-
// src/character/instancing/CharacterColourSamplingUtils.ts
|
1873
1731
|
function listAllBoneNames(obj) {
|
1874
1732
|
const boneNames = [];
|
1875
1733
|
if (!obj) {
|
@@ -2020,26 +1878,14 @@ function captureCharacterColorsFromObject3D(object3D, options) {
|
|
2020
1878
|
clone3.scale.set(1, 1, 1);
|
2021
1879
|
const skinnedMeshes = [];
|
2022
1880
|
clone3.traverse((child) => {
|
2023
|
-
if (child instanceof
|
2024
|
-
|
1881
|
+
if (child instanceof SkinnedMesh || child.isSkinnedMesh) {
|
1882
|
+
const skinnedMesh = child;
|
1883
|
+
skinnedMesh.skeleton.pose();
|
1884
|
+
skinnedMesh.skeleton.update();
|
1885
|
+
skinnedMesh.updateMatrixWorld(true);
|
2025
1886
|
}
|
2026
1887
|
});
|
2027
|
-
|
2028
|
-
console.warn("No SkinnedMesh objects found in Object3D hierarchy");
|
2029
|
-
return /* @__PURE__ */ new Map();
|
2030
|
-
}
|
2031
|
-
let skinnedMesh;
|
2032
|
-
if (skinnedMeshes.length === 1) {
|
2033
|
-
skinnedMesh = skinnedMeshes[0];
|
2034
|
-
} else {
|
2035
|
-
skinnedMesh = mergeSkinnedMeshes(skinnedMeshes);
|
2036
|
-
}
|
2037
|
-
const skeleton = skinnedMesh.skeleton;
|
2038
|
-
skeleton.pose();
|
2039
|
-
skeleton.update();
|
2040
|
-
skinnedMesh.updateMatrixWorld(true);
|
2041
|
-
validateAndCleanSkeleton(skinnedMesh);
|
2042
|
-
return captureCharacterColors(skinnedMesh, options);
|
1888
|
+
return captureCharacterColors(object3D, options);
|
2043
1889
|
}
|
2044
1890
|
function findBoneCenter(bone) {
|
2045
1891
|
const boneStart = new Vector32();
|
@@ -2054,9 +1900,20 @@ function findBoneCenter(bone) {
|
|
2054
1900
|
}
|
2055
1901
|
function getBoneRegionsForColorSampling(characterMesh, camera, renderSize, circularSamplingRadius, topDownSamplingSize, debug) {
|
2056
1902
|
const regions = [];
|
1903
|
+
let firstSkinnedMesh = null;
|
1904
|
+
characterMesh.traverse((child) => {
|
1905
|
+
if (!firstSkinnedMesh && (child instanceof SkinnedMesh || child.isSkinnedMesh)) {
|
1906
|
+
firstSkinnedMesh = child;
|
1907
|
+
}
|
1908
|
+
});
|
1909
|
+
if (!firstSkinnedMesh) {
|
1910
|
+
console.warn("No SkinnedMesh objects found in Object3D hierarchy");
|
1911
|
+
return regions;
|
1912
|
+
}
|
1913
|
+
const skinnedMesh = firstSkinnedMesh;
|
2057
1914
|
if (debug) {
|
2058
1915
|
console.log("Available bones:");
|
2059
|
-
console.table(listAllBoneNames(
|
1916
|
+
console.table(listAllBoneNames(skinnedMesh));
|
2060
1917
|
}
|
2061
1918
|
const boneTargets = [
|
2062
1919
|
{ name: "Face/Chin", boneName: "neck_02" },
|
@@ -2075,7 +1932,7 @@ function getBoneRegionsForColorSampling(characterMesh, camera, renderSize, circu
|
|
2075
1932
|
];
|
2076
1933
|
const screenPos = new Vector32();
|
2077
1934
|
for (const target of boneTargets) {
|
2078
|
-
const bone =
|
1935
|
+
const bone = skinnedMesh.skeleton.bones.find(
|
2079
1936
|
(child) => child.name === target.boneName
|
2080
1937
|
);
|
2081
1938
|
if (bone) {
|
@@ -2669,7 +2526,7 @@ var CharacterModel = class {
|
|
2669
2526
|
this.animationMixer = null;
|
2670
2527
|
}
|
2671
2528
|
(_a = this.mesh) == null ? void 0 : _a.traverse((child) => {
|
2672
|
-
if (child instanceof
|
2529
|
+
if (child instanceof SkinnedMesh2 || child.isSkinnedMesh) {
|
2673
2530
|
const asSkinnedMesh = child;
|
2674
2531
|
if (asSkinnedMesh.geometry) {
|
2675
2532
|
asSkinnedMesh.geometry.dispose();
|
@@ -3391,6 +3248,146 @@ function getTrackTypeFromName(trackName) {
|
|
3391
3248
|
return NumberKeyframeTrack;
|
3392
3249
|
}
|
3393
3250
|
|
3251
|
+
// src/character/instancing/CharacterInstancingUtils.ts
|
3252
|
+
import { BufferAttribute, SkinnedMesh as SkinnedMesh3 } from "three";
|
3253
|
+
import * as BufferGeometryUtils from "three/examples/jsm/utils/BufferGeometryUtils.js";
|
3254
|
+
function mergeSkinnedMeshes(skinnedMeshes, debug) {
|
3255
|
+
const geometries = [];
|
3256
|
+
const materials = [];
|
3257
|
+
const skeleton = skinnedMeshes[0].skeleton;
|
3258
|
+
for (const skinnedMesh of skinnedMeshes) {
|
3259
|
+
const geometry = skinnedMesh.geometry.clone();
|
3260
|
+
const materialIndex = materials.length;
|
3261
|
+
if (Array.isArray(skinnedMesh.material)) {
|
3262
|
+
materials.push(...skinnedMesh.material);
|
3263
|
+
for (let i = 0; i < skinnedMesh.material.length; i++) {
|
3264
|
+
geometry.addGroup(
|
3265
|
+
0,
|
3266
|
+
geometry.index ? geometry.index.count : geometry.attributes.position.count,
|
3267
|
+
materialIndex + i
|
3268
|
+
);
|
3269
|
+
}
|
3270
|
+
} else {
|
3271
|
+
materials.push(skinnedMesh.material);
|
3272
|
+
geometry.addGroup(
|
3273
|
+
0,
|
3274
|
+
geometry.index ? geometry.index.count : geometry.attributes.position.count,
|
3275
|
+
materialIndex
|
3276
|
+
);
|
3277
|
+
}
|
3278
|
+
geometries.push(geometry);
|
3279
|
+
}
|
3280
|
+
const mergedGeometry = BufferGeometryUtils.mergeGeometries(geometries, true);
|
3281
|
+
if (!mergedGeometry) {
|
3282
|
+
throw new Error("Failed to merge geometries");
|
3283
|
+
}
|
3284
|
+
const mergedMesh = new SkinnedMesh3(mergedGeometry, materials);
|
3285
|
+
mergedMesh.skeleton = skeleton;
|
3286
|
+
mergedMesh.bindMatrix.copy(skinnedMeshes[0].bindMatrix);
|
3287
|
+
mergedMesh.bindMatrixInverse.copy(skinnedMeshes[0].bindMatrixInverse);
|
3288
|
+
mergedMesh.bind(skeleton, mergedMesh.bindMatrix);
|
3289
|
+
if (debug) {
|
3290
|
+
console.log(`Merged into single mesh with ${materials.length} materials`);
|
3291
|
+
}
|
3292
|
+
addVertexColorsToGeometry(mergedGeometry, materials, debug);
|
3293
|
+
return mergedMesh;
|
3294
|
+
}
|
3295
|
+
function validateAndCleanSkeleton(skinnedMesh) {
|
3296
|
+
const skeleton = skinnedMesh.skeleton;
|
3297
|
+
const nullBoneIndices = [];
|
3298
|
+
for (let i = 0; i < skeleton.bones.length; i++) {
|
3299
|
+
if (!skeleton.bones[i]) {
|
3300
|
+
nullBoneIndices.push(i);
|
3301
|
+
}
|
3302
|
+
}
|
3303
|
+
if (nullBoneIndices.length > 0) {
|
3304
|
+
skeleton.bones = skeleton.bones.filter((bone) => bone !== null && bone !== void 0);
|
3305
|
+
skeleton.update();
|
3306
|
+
}
|
3307
|
+
}
|
3308
|
+
function addVertexColorsToGeometry(geometry, materials, debug) {
|
3309
|
+
const positionAttribute = geometry.getAttribute("position");
|
3310
|
+
if (!positionAttribute) {
|
3311
|
+
console.error("No position attribute found in geometry");
|
3312
|
+
return;
|
3313
|
+
}
|
3314
|
+
const vertexCount = positionAttribute.count;
|
3315
|
+
if (debug) {
|
3316
|
+
console.log(`Geometry has ${vertexCount} vertices`);
|
3317
|
+
}
|
3318
|
+
const colors = new Float32Array(vertexCount * 3);
|
3319
|
+
for (let i = 0; i < vertexCount; i++) {
|
3320
|
+
colors[i * 3] = 1;
|
3321
|
+
colors[i * 3 + 1] = 1;
|
3322
|
+
colors[i * 3 + 2] = 1;
|
3323
|
+
}
|
3324
|
+
const materialColorCodes = {
|
3325
|
+
// bin material IDs to be replaced by the final colors on the GPU
|
3326
|
+
hair: [0, 0, 0],
|
3327
|
+
shirt_short: [0, 0, 1],
|
3328
|
+
shirt_long: [0, 1, 0],
|
3329
|
+
pants_short: [0, 1, 1],
|
3330
|
+
pants_long: [1, 0, 0],
|
3331
|
+
shoes: [1, 0, 1],
|
3332
|
+
skin: [1, 1, 0],
|
3333
|
+
lips: [1, 1, 1],
|
3334
|
+
eyes_black: [0.5, 0, 0],
|
3335
|
+
eyes_white: [0, 0.5, 0]
|
3336
|
+
};
|
3337
|
+
if (debug) {
|
3338
|
+
console.log("Geometry groups:", geometry.groups);
|
3339
|
+
console.log(
|
3340
|
+
"Materials:",
|
3341
|
+
materials.map((m) => m.name)
|
3342
|
+
);
|
3343
|
+
}
|
3344
|
+
if (geometry.groups && geometry.groups.length > 0) {
|
3345
|
+
geometry.groups.forEach((group, groupIndex) => {
|
3346
|
+
const material = materials[group.materialIndex || groupIndex];
|
3347
|
+
const materialName = (material == null ? void 0 : material.name) || `material_${groupIndex}`;
|
3348
|
+
if (debug) {
|
3349
|
+
console.log(
|
3350
|
+
`Processing group ${groupIndex}: material "${materialName}", start: ${group.start}, count: ${group.count}`
|
3351
|
+
);
|
3352
|
+
}
|
3353
|
+
const materialColor = materialColorCodes[materialName] || [
|
3354
|
+
1,
|
3355
|
+
1,
|
3356
|
+
1
|
3357
|
+
];
|
3358
|
+
if (debug) {
|
3359
|
+
console.log(
|
3360
|
+
`Using ID color [${materialColor.join(", ")}] for material "${materialName}" (${materialColor[0] === 1 && materialColor[1] === 1 && materialColor[2] === 0 ? "YELLOW=SKIN" : materialColor[0] === 0 && materialColor[1] === 0 && materialColor[2] === 1 ? "BLUE=SHIRT" : "OTHER"})`
|
3361
|
+
);
|
3362
|
+
}
|
3363
|
+
const indexAttribute = geometry.getIndex();
|
3364
|
+
if (indexAttribute) {
|
3365
|
+
for (let i = group.start; i < group.start + group.count; i++) {
|
3366
|
+
const vertexIndex = indexAttribute.getX(i);
|
3367
|
+
colors[vertexIndex * 3] = materialColor[0];
|
3368
|
+
colors[vertexIndex * 3 + 1] = materialColor[1];
|
3369
|
+
colors[vertexIndex * 3 + 2] = materialColor[2];
|
3370
|
+
}
|
3371
|
+
} else {
|
3372
|
+
const startVertex = group.start / 3;
|
3373
|
+
const vertexCount2 = group.count / 3;
|
3374
|
+
for (let i = 0; i < vertexCount2; i++) {
|
3375
|
+
const vertexIndex = startVertex + i;
|
3376
|
+
colors[vertexIndex * 3] = materialColor[0];
|
3377
|
+
colors[vertexIndex * 3 + 1] = materialColor[1];
|
3378
|
+
colors[vertexIndex * 3 + 2] = materialColor[2];
|
3379
|
+
}
|
3380
|
+
}
|
3381
|
+
});
|
3382
|
+
} else {
|
3383
|
+
console.warn("No geometry groups found, using single material coloring");
|
3384
|
+
}
|
3385
|
+
geometry.setAttribute("color", new BufferAttribute(colors, 3));
|
3386
|
+
if (debug) {
|
3387
|
+
console.log(`Added per-material vertex colors to ${vertexCount} vertices`);
|
3388
|
+
}
|
3389
|
+
}
|
3390
|
+
|
3394
3391
|
// src/character/instancing/vendor/core/InstancedEntity.ts
|
3395
3392
|
import {
|
3396
3393
|
Euler,
|