Skip to content

Commit

Permalink
Merge pull request #62 from M3-org/optimizer-improve-3
Browse files Browse the repository at this point in the history
Optimizer improve 3
  • Loading branch information
madjin authored Nov 9, 2023
2 parents a284b99 + 17eea2e commit 1ed89ea
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 53 deletions.
20 changes: 11 additions & 9 deletions src/components/Slider.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,17 @@ export default function Slider ({ value, min, max, onChange, step, stepBox }) {
className={styles["slider"]}
step ={step}
/>
<input
type="number"
min="0"
max="100"
value={value}
onChange={onChange}
className={styles["input-box"]}
step ={stepBox}
/>
{stepBox &&
<input
type="number"
min="0"
max="100"
value={value}
onChange={onChange}
className={styles["input-box"]}
step ={stepBox}
/>
}
</div>
);
};
Expand Down
218 changes: 185 additions & 33 deletions src/library/VRMExporterv0.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,12 @@ export default class VRMExporterv0 {
? object.children[0]
: object);
const attributes = mesh.geometry.attributes;
meshDatas.push(new MeshData(attributes.position, WEBGL_CONST.FLOAT, MeshDataType.POSITION, AccessorsType.VEC3, mesh.name, undefined));
meshDatas.push(new MeshData(attributes.normal, WEBGL_CONST.FLOAT, MeshDataType.NORMAL, AccessorsType.VEC3, mesh.name, undefined));
const positionAttribute = new MeshData(attributes.position, WEBGL_CONST.FLOAT, MeshDataType.POSITION, AccessorsType.VEC3, mesh.name, undefined);
meshDatas.push(positionAttribute);

const normalAttribute = new MeshData(attributes.normal, WEBGL_CONST.FLOAT, MeshDataType.NORMAL, AccessorsType.VEC3, mesh.name, undefined);
meshDatas.push(normalAttribute);

meshDatas.push(new MeshData(attributes.uv, WEBGL_CONST.FLOAT, MeshDataType.UV, AccessorsType.VEC2, mesh.name, undefined));
meshDatas.push(new MeshData(attributes.skinWeight, WEBGL_CONST.FLOAT, MeshDataType.SKIN_WEIGHT, AccessorsType.VEC4, mesh.name, undefined));
meshDatas.push(new MeshData(attributes.skinIndex, WEBGL_CONST.UNSIGNED_SHORT, MeshDataType.SKIN_INDEX, AccessorsType.VEC4, mesh.name, undefined));
Expand All @@ -219,6 +223,7 @@ export default class VRMExporterv0 {
}

mesh.geometry.userData.targetNames = [];
console.warn("taking only mesh 0 for morph targets now, take the correct mesh");
for (const prop in vrm.expressionManager.expressionMap){
const expression = vrm.expressionManager.expressionMap[prop];
const morphTargetBinds = expression._binds.map(obj => ({mesh:0, index:obj.index, weight:obj.weight * 100 }))
Expand Down Expand Up @@ -250,23 +255,54 @@ export default class VRMExporterv0 {
// to do, material target binds, and texture transform binds
}

for (const prop in mesh.morphTargetDictionary){

mesh.geometry.userData.targetNames.push(prop);
const morphIndex = mesh.morphTargetDictionary[prop];
const morphAttribute = mesh.geometry.morphAttributes;
meshDatas.push(new MeshData(morphAttribute.position[morphIndex], WEBGL_CONST.FLOAT, MeshDataType.BLEND_POSITION, AccessorsType.VEC3, mesh.name, BLENDSHAPE_PREFIX + prop));
const getMorphData = ( attributeData , prop , meshDataType, baseAttribute) => {
const nonZeroIndices = [];
const nonZeroValues = [];

if (morphAttribute.normal){
// Step 1: Get Zero Elements
for (let i = 0; i < attributeData.length; i += 3) {
const x = attributeData[i];
const y = attributeData[i + 1];
const z = attributeData[i + 2];

// Check if any of the x, y, or z values is non-zero
if (x !== 0 || y !== 0 || z !== 0) {
nonZeroIndices.push(i / 3); // Push the index of the position, not the index in the array
nonZeroValues.push(x, y, z);
}
}
if (nonZeroIndices.length > 0){
// Step 2: Create sparse data
const sparseData = {
count: nonZeroIndices.length, // Total number of position elements
indices: new Uint32Array(nonZeroIndices),
values: new Float32Array(nonZeroValues),
};

// Step 3: Create MeshData
meshDatas.push(new MeshData(
morphAttribute.normal[morphIndex],
baseAttribute,
WEBGL_CONST.FLOAT,
MeshDataType.BLEND_NORMAL,
meshDataType,
AccessorsType.VEC3,
mesh.name,
BLENDSHAPE_PREFIX + prop));
}
BLENDSHAPE_PREFIX + prop,
sparseData));
}
}

for (const prop in mesh.morphTargetDictionary){

mesh.geometry.userData.targetNames.push(prop);
const morphIndex = mesh.morphTargetDictionary[prop];
const morphAttribute = mesh.geometry.morphAttributes;

getMorphData(morphAttribute.position[morphIndex].array, prop , MeshDataType.BLEND_POSITION , attributes.position)

if (morphAttribute.normal)
getMorphData(morphAttribute.normal[morphIndex].array, prop , MeshDataType.BLEND_NORMAL , attributes.normal)

}
});
// inverseBindMatrices length = 16(matrixの要素数) * 4バイト * ボーン数
// TODO: とりあえず数合わせでrootNode以外のBoneのmatrixをいれた
Expand All @@ -278,7 +314,7 @@ export default class VRMExporterv0 {
meshDatas.push(new MeshData(new BufferAttribute(inverseBindMatrices, 16), WEBGL_CONST.FLOAT, MeshDataType.BIND_MATRIX, AccessorsType.MAT4, mesh.name, mesh.name));
});
outputAccessors.push(...meshDatas.map((meshData) => ({
bufferView: -1,
// bufferView: -1,
byteOffset: 0,
componentType: meshData.valueType,
count: meshData.attribute.count,
Expand Down Expand Up @@ -495,7 +531,18 @@ export default class VRMExporterv0 {
buffer: imageBitmap2png(image.imageBitmap),
type: MeshDataType.IMAGE,
})));
bufferViews.push(...meshDatas.map((data) => ({ buffer: data.buffer, type: data.type })));

// bufferViews.push(...meshDatas.map((data) => ({ buffer: data.buffer, type: data.type })));
meshDatas.forEach((data, i) => {
if (data.buffer) {
bufferViews.push({ buffer: data.buffer, typeString:"", type: data.type });
} else if (data.sparse) {
bufferViews.push({ buffer: data.sparse.indices, typeString:"indices", type: data.type, count:data.sparse.count });
bufferViews.push({ buffer: data.sparse.values, typeString:"values",type: data.type });
}
});


if (icon)
bufferViews.push({
buffer: imageBitmap2png(icon.imageBitmap),
Expand All @@ -504,7 +551,9 @@ export default class VRMExporterv0 {
let bufferOffset = 0;
let imageIndex = 0;
let accessorIndex = 0;
const outputBufferViews = bufferViews.map((bufferView, index) => {

let index = 0;
const outputBufferViews = bufferViews.map((bufferView) => {
const value = {
buffer: 0,
byteLength: bufferView.buffer.byteLength,
Expand All @@ -519,13 +568,62 @@ export default class VRMExporterv0 {
bufferOffset += bufferView.buffer.byteLength;
if (bufferView.type === MeshDataType.IMAGE) {
outputImages[imageIndex++].bufferView = index;
index++;
}
else {
outputAccessors[accessorIndex++].bufferView = index;
if (!meshDatas[accessorIndex].sparse){
meshDatas[accessorIndex].bufferIndex = index;

// save the bufferview in case we need it for sparse accessors
outputAccessors[accessorIndex].bufferView = index;

accessorIndex++
index++;
}
else{
//const bufferIndex = meshDatas[accessorIndex]?.sparse?.sparseAttribute?.bufferIndex;

// apply index from sparseAttribute
//outputAccessors[accessorIndex].bufferView = bufferIndex; //

// create the sparse object if it has not been created yet
if (outputAccessors[accessorIndex].sparse == null)
outputAccessors[accessorIndex].sparse = {}

// if the buffer view is representing indices of the sparse, save them into an indices object
// also save count, we can take the length of the indicesw view for this
if (bufferView.typeString === "indices"){
outputAccessors[accessorIndex].sparse.count = bufferView.count;
outputAccessors[accessorIndex].sparse[bufferView.typeString] = {
bufferView : index,
byteOffset : 0,
componentType : WEBGL_CONST.UNSIGNED_INT
}
}
if (bufferView.typeString === "values"){
outputAccessors[accessorIndex].sparse[bufferView.typeString] = {
bufferView : index,
byteOffset : 0,
componentType : WEBGL_CONST.FLOAT
}
}

//outputAccessors[accessorIndex].sparse

// add accessor index only if this is the last sparse type value
if (bufferView.typeString === "values"){
accessorIndex++;
}

// always add to index
index++;
}
}
return value;
});

const outputScenes = toOutputScenes(avatar, outputNodes);

const outputData = {
accessors: outputAccessors,
asset: exporterInfo,
Expand Down Expand Up @@ -570,6 +668,7 @@ export default class VRMExporterv0 {
skins: outputSkins,
textures: outputTextures,
};
console.log(outputData)
const jsonChunk = new GlbChunk(parseString2Binary(JSON.stringify(outputData, undefined, 2)), "JSON");
const binaryChunk = new GlbChunk(concatBinary(bufferViews.map((buf) => buf.buffer)), "BIN\x00");
const fileData = concatBinary([jsonChunk.buffer, binaryChunk.buffer]);
Expand Down Expand Up @@ -687,30 +786,73 @@ class GlbChunk {
}
}
export class MeshData {
constructor(attribute, valueType, type, accessorsType, meshName, name) {
constructor(attribute, valueType, type, accessorsType, meshName, name, sparseData) {
this.attribute = attribute;
this.type = type;
this.valueType = valueType;
this.accessorsType = accessorsType;
this.meshName = meshName;
this.name = name;
this.buffer = parseBinary(this.attribute, this.valueType);
this.max =

// Check if sparse data is provided

if (sparseData) {
const { indices, values, count } =sparseData;

// Convert indices and values to BufferAttributes
const indicesBufferAttribute = new BufferAttribute(
indices,
1 // Set the item size to 1 for indices
);
const valuesBufferAttribute = new BufferAttribute(
values,
attribute.itemSize // Use the same item size as the original attribute
);
// pass as attribute
this.sparse = {
count,
indices:parseBinary(indicesBufferAttribute, WEBGL_CONST.UNSIGNED_INT), // detect if use WEBGL_CONST.UNSIGNED_SHORT or WEBGL_CONST.UNSIGNED_INT
values:parseBinary(valuesBufferAttribute, WEBGL_CONST.FLOAT)
}

this.max =
type === MeshDataType.POSITION || type === MeshDataType.BLEND_POSITION
? [
Math.max.apply(null, Array.from(values).filter((_, i) => i % 3 === 0)),
Math.max.apply(null, Array.from(values).filter((_, i) => i % 3 === 1)),
Math.max.apply(null, Array.from(values).filter((_, i) => i % 3 === 2)),
]
: undefined;
this.min =
type === MeshDataType.POSITION || type === MeshDataType.BLEND_POSITION
? [
Math.min.apply(null, Array.from(values).filter((_, i) => i % 3 === 0)),
Math.min.apply(null, Array.from(values).filter((_, i) => i % 3 === 1)),
Math.min.apply(null, Array.from(values).filter((_, i) => i % 3 === 2)),
]
: undefined;
}
else{
this.buffer = parseBinary(this.attribute, this.valueType)

this.max =
type === MeshDataType.POSITION || type === MeshDataType.BLEND_POSITION
? [
Math.max.apply(null, Array.from(this.attribute.array).filter((_, i) => i % 3 === 0)),
Math.max.apply(null, Array.from(this.attribute.array).filter((_, i) => i % 3 === 1)),
Math.max.apply(null, Array.from(this.attribute.array).filter((_, i) => i % 3 === 2)),
]
: undefined;
this.min =
this.min =
type === MeshDataType.POSITION || type === MeshDataType.BLEND_POSITION
? [
Math.min.apply(null, Array.from(this.attribute.array).filter((_, i) => i % 3 === 0)),
Math.min.apply(null, Array.from(this.attribute.array).filter((_, i) => i % 3 === 1)),
Math.min.apply(null, Array.from(this.attribute.array).filter((_, i) => i % 3 === 2)),
]
: undefined;
}

}
}
var MaterialType;
Expand Down Expand Up @@ -784,20 +926,30 @@ const toOutputMeshes = (meshes, meshDatas, uniqueMaterialNames) => {
material: uniqueMaterialNames.indexOf(materialName),
mode: 4,
targets: mesh.geometry.userData.targetNames
? mesh.geometry.userData.targetNames.map((targetName) => ({
NORMAL: meshDatas
? mesh.geometry.userData.targetNames.map((targetName) => {
const normalIndex = meshDatas
.map((data) => data.type === MeshDataType.BLEND_NORMAL &&
data.meshName === mesh.name
? data.name
: null)
.indexOf(BLENDSHAPE_PREFIX + targetName),
POSITION: meshDatas
data.meshName === mesh.name
? data.name
: null)
.indexOf(BLENDSHAPE_PREFIX + targetName);

const positionIndex = meshDatas
.map((data) => data.type === MeshDataType.BLEND_POSITION &&
data.meshName === mesh.name
? data.name
: null)
.indexOf(BLENDSHAPE_PREFIX + targetName),
}))
data.meshName === mesh.name
? data.name
: null)
.indexOf(BLENDSHAPE_PREFIX + targetName);


const result = {}
if (positionIndex !== -1)
result.POSITION = positionIndex;
if (normalIndex !== -1)
result.NORMAL = normalIndex;
// Use the indices or handle the case when they are -1
return result;
})
: undefined,
};
}),
Expand Down
17 changes: 8 additions & 9 deletions src/library/create-texture-atlas.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,16 @@ function RenderTextureImageData(texture, multiplyColor, clearColor, width, heigh

sceneRTT.add(cameraRTT);

rtTexture = new THREE.WebGLRenderTarget(width, height);
rtTexture.texture.encoding = THREE.sRGBEncoding;

material = new THREE.MeshBasicMaterial({
side: THREE.DoubleSide,
transparent: true,
opacity: 1,
color: new THREE.Color(1, 1, 1),
});

const plane = new THREE.PlaneGeometry(width, height);
const plane = new THREE.PlaneGeometry(1, 1);
quad = new THREE.Mesh(plane, material);
quad.scale.set(width,height,1);
sceneRTT.add(quad);

renderer = new THREE.WebGLRenderer();
Expand All @@ -48,14 +46,15 @@ function RenderTextureImageData(texture, multiplyColor, clearColor, width, heigh
cameraRTT.top = height / 2;
cameraRTT.bottom = - height / 2;

const plane = new THREE.PlaneGeometry(width, height);
quad = new THREE.Mesh(plane, material);

rtTexture.width = width;
rtTexture.height = height;
cameraRTT.updateProjectionMatrix();
quad.scale.set(width,height,1)

renderer.setSize(width, height);
}

rtTexture = new THREE.WebGLRenderTarget(width, height);
rtTexture.texture.encoding = THREE.sRGBEncoding;

material.map = texture;
material.color = multiplyColor.clone();
renderer.setClearColor(clearColor.clone(), 1);
Expand Down
Loading

0 comments on commit 1ed89ea

Please sign in to comment.