Ergänze 3D-Vorschau zur individuellen Haar- und Augenfarbe
All checks were successful
Deploy to production / deploy (push) Successful in 2m36s
All checks were successful
Deploy to production / deploy (push) Successful in 2m36s
This commit is contained in:
76
frontend/scripts/test-character-appearance.mjs
Normal file
76
frontend/scripts/test-character-appearance.mjs
Normal file
@@ -0,0 +1,76 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { BoxGeometry, Group, Mesh, MeshStandardMaterial, Vector3 } from 'three';
|
||||
import {
|
||||
applyCharacterAppearance, captureAppearanceEditor, getAppearanceRegion,
|
||||
getAppearanceStats, paintAppearanceRegion, prepareAppearanceEditor, restoreAppearanceEditor,
|
||||
} from '../src/utils/characterAppearance.js';
|
||||
|
||||
function fixture(material = new MeshStandardMaterial()) {
|
||||
const model = new Group();
|
||||
const mesh = new Mesh(new BoxGeometry(), material);
|
||||
model.add(mesh);
|
||||
model.updateMatrixWorld(true);
|
||||
return { model, mesh };
|
||||
}
|
||||
|
||||
test('painting separates material regions while preserving the original mesh and UVs', () => {
|
||||
const { model, mesh } = fixture();
|
||||
const original = mesh.geometry;
|
||||
const originalUV = original.getAttribute('uv').array.slice();
|
||||
prepareAppearanceEditor(model);
|
||||
assert.notEqual(mesh.geometry, original);
|
||||
assert.deepEqual(getAppearanceStats(model), { body: 12, hair: 0, iris: 0 });
|
||||
const snapshot = captureAppearanceEditor(model);
|
||||
const indexBuffer = mesh.geometry.index;
|
||||
assert.equal(paintAppearanceRegion({ object: mesh, faceIndex: 0, point: new Vector3(.5, 0, 0) }, 0, 'hair', new Vector3(-1, 0, 0)), true);
|
||||
assert.deepEqual(getAppearanceStats(model), { body: 11, hair: 1, iris: 0 });
|
||||
assert.deepEqual(mesh.geometry.getAttribute('uv').array, originalUV);
|
||||
assert.equal(mesh.geometry.index, indexBuffer);
|
||||
assert.equal(original.groups.length, 6);
|
||||
restoreAppearanceEditor(snapshot);
|
||||
assert.deepEqual(getAppearanceStats(model), { body: 12, hair: 0, iris: 0 });
|
||||
assert.equal(paintAppearanceRegion({ object: mesh, faceIndex: 0, point: new Vector3() }, 1, 'unknown', new Vector3()), false);
|
||||
});
|
||||
|
||||
test('hair and iris colors remain independent of other instances and can return to the original', () => {
|
||||
const originalMaterial = new MeshStandardMaterial();
|
||||
const first = fixture(originalMaterial);
|
||||
const second = fixture(originalMaterial);
|
||||
prepareAppearanceEditor(first.model);
|
||||
prepareAppearanceEditor(second.model);
|
||||
applyCharacterAppearance(first.model, { hairColor: '#ff0000', eyeColor: '#0000ff' });
|
||||
applyCharacterAppearance(second.model, { hairColor: '#00ff00' });
|
||||
const shader = () => ({ uniforms: {}, fragmentShader: '#include <map_fragment>\n#include <emissivemap_fragment>' });
|
||||
const hairShader = shader(), irisShader = shader(), otherShader = shader();
|
||||
first.mesh.material[1].onBeforeCompile(hairShader);
|
||||
first.mesh.material[2].onBeforeCompile(irisShader);
|
||||
second.mesh.material[1].onBeforeCompile(otherShader);
|
||||
assert.equal(hairShader.uniforms.falukantTargetColor.value.getHexString(), 'ff0000');
|
||||
assert.equal(irisShader.uniforms.falukantTargetColor.value.getHexString(), '0000ff');
|
||||
assert.equal(otherShader.uniforms.falukantTargetColor.value.getHexString(), '00ff00');
|
||||
applyCharacterAppearance(first.model, { hairColor: '#cccccc', eyeColor: null });
|
||||
assert.equal(hairShader.uniforms.falukantTargetColor.value.getHexString(), 'cccccc');
|
||||
assert.equal(irisShader.uniforms.falukantColorEnabled.value, 0);
|
||||
assert.equal(otherShader.uniforms.falukantTargetColor.value.getHexString(), '00ff00');
|
||||
applyCharacterAppearance(first.model, { hairColor: 'invalid' });
|
||||
assert.equal(hairShader.uniforms.falukantColorEnabled.value, 0);
|
||||
});
|
||||
|
||||
test('saved glTF material metadata can be prepared again without multiplying materials', () => {
|
||||
const first = fixture();
|
||||
prepareAppearanceEditor(first.model);
|
||||
const model = new Group();
|
||||
const mesh = new Mesh(first.mesh.geometry.clone(), first.mesh.material.map(material => {
|
||||
const copy = material.clone();
|
||||
copy.userData = JSON.parse(JSON.stringify(material.userData));
|
||||
return copy;
|
||||
}));
|
||||
model.add(mesh);
|
||||
prepareAppearanceEditor(model);
|
||||
assert.equal(mesh.material.length, 3);
|
||||
assert.equal(getAppearanceRegion(mesh.material[1]), 'hair');
|
||||
assert.equal(getAppearanceRegion(mesh.material[2]), 'iris');
|
||||
prepareAppearanceEditor(model);
|
||||
assert.equal(mesh.material.length, 3);
|
||||
});
|
||||
Reference in New Issue
Block a user