Skip to content

Commit

Permalink
sparse accessor export start
Browse files Browse the repository at this point in the history
memelotsqui committed Nov 9, 2023
1 parent ecc779e commit 764f9ee
Showing 1 changed file with 189 additions and 16 deletions.
205 changes: 189 additions & 16 deletions src/library/VRMExporterv0.js
Original file line number Diff line number Diff line change
@@ -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));
@@ -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 }))
@@ -250,23 +255,97 @@ export default class VRMExporterv0 {
// to do, material target binds, and texture transform binds
}

const getMorphData = ( attributeData , prop , sparseAttribute , meshDataType) => {
const nonZeroIndices = [];
const nonZeroValues = [];

// 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);
}
}

// Step 2: Create sparse data
const sparseData = {
sparseAttribute,
count: nonZeroIndices.length, // Total number of position elements
indices: new Uint32Array(nonZeroIndices),
values: new Float32Array(nonZeroValues),
};

// Step 3: Create MeshData
meshDatas.push(new MeshData(
attributes.position,
WEBGL_CONST.FLOAT,
meshDataType,
AccessorsType.VEC3,
mesh.name,
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;
meshDatas.push(new MeshData(morphAttribute.position[morphIndex], WEBGL_CONST.FLOAT, MeshDataType.BLEND_POSITION, AccessorsType.VEC3, mesh.name, BLENDSHAPE_PREFIX + prop));
//console.log("morph postion", morphAttribute.position[morphIndex]);

getMorphData(morphAttribute.position[morphIndex].array, prop , positionAttribute , MeshDataType.BLEND_POSITION )
getMorphData(morphAttribute.normal[morphIndex].array, prop , normalAttribute , MeshDataType.BLEND_NORMAL)

// const attributeData = morphAttribute.position[morphIndex].array;
// const nonZeroIndices = [];
// const nonZeroValues = [];

// 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);
// }
// }

// // Step 2: Create sparse data
// const sparseData = {
// sparseAttribute: positionAttribute,
// count: nonZeroIndices.length, // Total number of position elements
// indices: new Uint32Array(nonZeroIndices),
// values: new Float32Array(nonZeroValues),
// };


// meshDatas.push(new MeshData(
// morphAttribute.position[morphIndex],
// WEBGL_CONST.FLOAT,
// MeshDataType.BLEND_POSITION,
// AccessorsType.VEC3,
// mesh.name,
// BLENDSHAPE_PREFIX + prop,
// sparseData));

if (morphAttribute.normal){
meshDatas.push(new MeshData(
morphAttribute.normal[morphIndex],
WEBGL_CONST.FLOAT,
MeshDataType.BLEND_NORMAL,
AccessorsType.VEC3,
mesh.name,
BLENDSHAPE_PREFIX + prop));
}
console.warn("XXX to do", morphAttribute.normal);
// meshDatas.push(new MeshData(
// morphAttribute.normal[morphIndex],
// WEBGL_CONST.FLOAT,
// MeshDataType.BLEND_NORMAL,
// AccessorsType.VEC3,
// mesh.name,
// BLENDSHAPE_PREFIX + prop));
}
}
});
// inverseBindMatrices length = 16(matrixの要素数) * 4バイト * ボーン数
// TODO: とりあえず数合わせでrootNode以外のBoneのmatrixをいれた
@@ -495,7 +574,20 @@ 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) => {
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 });
}
});
console.log(bufferViews);
console.log(meshDatas);


if (icon)
bufferViews.push({
buffer: imageBitmap2png(icon.imageBitmap),
@@ -504,7 +596,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,
@@ -519,13 +613,65 @@ export default class VRMExporterv0 {
bufferOffset += bufferView.buffer.byteLength;
if (bufferView.type === MeshDataType.IMAGE) {
outputImages[imageIndex++].bufferView = index;
console.log("image index", 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"){
console.log(outputAccessors[accessorIndex]);
accessorIndex++;
}

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

console.log(outputBufferViews);
const outputScenes = toOutputScenes(avatar, outputNodes);

const outputData = {
accessors: outputAccessors,
asset: exporterInfo,
@@ -570,6 +716,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]);
@@ -687,14 +834,40 @@ 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);

// Check if sparse data is provided

if (sparseData) {
const { indices, values, count, sparseAttribute } =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
);
console.log(indices);
// pass as attribute
this.sparse = {
sparseAttribute,
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)
}
}
else{
this.buffer = parseBinary(this.attribute, this.valueType);
}
this.max =
type === MeshDataType.POSITION || type === MeshDataType.BLEND_POSITION
? [

0 comments on commit 764f9ee

Please sign in to comment.