245 lines
6.8 KiB
JavaScript
245 lines
6.8 KiB
JavaScript
let exporterAction;
|
||
|
||
function exportToDeerEngine() {
|
||
// Build your JSON structure and trigger download
|
||
var json = {
|
||
mesh: {
|
||
hasNormalData: true,
|
||
vertexPositions: [],
|
||
vertexNormals: [],
|
||
indices: [],
|
||
},
|
||
};
|
||
|
||
function addVertex(vector) {
|
||
json.mesh.vertexPositions.push({
|
||
x: Math.round((vector[0] / 16) * 256),
|
||
y: Math.round((vector[1] / 16) * 256),
|
||
z: Math.round((vector[2] / 16) * 256),
|
||
});
|
||
|
||
return json.mesh.vertexPositions.length - 1;
|
||
}
|
||
|
||
function addIndex(indexID) {
|
||
json.mesh.indices.push(indexID);
|
||
}
|
||
|
||
function addNormal(normal) {
|
||
json.mesh.vertexNormals.push({
|
||
x: Math.round(normal[0] * 64),
|
||
y: Math.round(normal[1] * 64),
|
||
z: Math.round(normal[2] * 64),
|
||
});
|
||
}
|
||
|
||
Mesh.all.forEach((mesh) => {
|
||
function getWorldPosition(v) {
|
||
const mat = mesh.mesh.matrixWorld; // mat4 (THREE.Matrix4)
|
||
const vec = new THREE.Vector3(v[0], v[1], v[2]);
|
||
vec.applyMatrix4(mat);
|
||
|
||
// Convert to your engine’s left-handed space: flip Y and Z
|
||
return [vec.x, vec.y, -vec.z];
|
||
}
|
||
|
||
for (const fkey in mesh.faces) {
|
||
const face = mesh.faces[fkey];
|
||
var verticesIDs = [];
|
||
|
||
for (const vKey of face.vertices) {
|
||
const position = getWorldPosition(mesh.vertices[vKey]);
|
||
verticesIDs.push(addVertex(position));
|
||
}
|
||
|
||
// DEER ENGINE IS CLOCK FACE
|
||
//for (var i = 0; i < verticesIDs.length; i++) {
|
||
// addIndex(verticesIDs[i]);
|
||
//}
|
||
|
||
if (verticesIDs.length == 3) {
|
||
addIndex(verticesIDs[0]);
|
||
addIndex(verticesIDs[2]);
|
||
addIndex(verticesIDs[1]);
|
||
} else if (verticesIDs.length == 4) {
|
||
addIndex(verticesIDs[0]);
|
||
addIndex(verticesIDs[2]);
|
||
addIndex(verticesIDs[1]);
|
||
|
||
addIndex(verticesIDs[1]);
|
||
addIndex(verticesIDs[2]);
|
||
addIndex(verticesIDs[3]);
|
||
}
|
||
|
||
// Normal calulation
|
||
var origin = getWorldPosition(mesh.vertices[face.vertices[0]]);
|
||
var dirBRef = getWorldPosition(mesh.vertices[face.vertices[1]]);
|
||
var dirARef = getWorldPosition(mesh.vertices[face.vertices[2]]);
|
||
var dirA = [0, 0, 0];
|
||
var dirB = [0, 0, 0];
|
||
for (var i = 0; i < 3; i++) {
|
||
dirB[i] = dirARef[i] - origin[i];
|
||
dirA[i] = dirBRef[i] - origin[i];
|
||
}
|
||
|
||
var normalDir = [0, 0, 0];
|
||
normalDir[0] = dirB[1] * dirA[2] - dirB[2] * dirA[1];
|
||
normalDir[1] = dirB[2] * dirA[0] - dirB[0] * dirA[2];
|
||
normalDir[2] = dirB[0] * dirA[1] - dirB[1] * dirA[0];
|
||
|
||
var normalMagnitude = Math.sqrt(
|
||
normalDir[0] * normalDir[0] +
|
||
normalDir[1] * normalDir[1] +
|
||
normalDir[2] * normalDir[2]
|
||
);
|
||
|
||
for (var i = 0; i < 3; i++) {
|
||
normalDir[i] = normalDir[i] / normalMagnitude;
|
||
}
|
||
|
||
for (var i = 0; i < verticesIDs.length; i++) {
|
||
addNormal(normalDir);
|
||
}
|
||
}
|
||
});
|
||
|
||
const face_corners = {
|
||
north: [5, 4, 7, 6],
|
||
east: [1, 5, 3, 7],
|
||
south: [0, 1, 2, 3],
|
||
west: [4, 0, 6, 2],
|
||
up: [2, 3, 6, 7],
|
||
down: [4, 5, 0, 1],
|
||
};
|
||
|
||
Cube.all.forEach((cube) => {
|
||
// Cubes are simpler: they have 6 faces and 8 vertices
|
||
const mat = cube.mesh.matrixWorld;
|
||
|
||
function getWorldPosition(v) {
|
||
const vec = new THREE.Vector3(v[0], v[1], v[2]);
|
||
vec.applyMatrix4(mat);
|
||
return [vec.x, vec.y, -vec.z]; // Flip Z to match left-handed
|
||
}
|
||
|
||
// cube.from and cube.to define the min and max corners
|
||
const from = cube.from; // [x, y, z]
|
||
const to = cube.to; // [x, y, z]
|
||
const corners = [
|
||
[from[0], from[1], from[2]],
|
||
[to[0], from[1], from[2]],
|
||
[from[0], to[1], from[2]],
|
||
[to[0], to[1], from[2]],
|
||
[from[0], from[1], to[2]],
|
||
[to[0], from[1], to[2]],
|
||
[from[0], to[1], to[2]],
|
||
[to[0], to[1], to[2]],
|
||
];
|
||
|
||
for (var i = 0; i < corners.length; i++) {
|
||
corners[i][0] -= cube.origin[0];
|
||
corners[i][1] -= cube.origin[1];
|
||
corners[i][2] -= cube.origin[2];
|
||
}
|
||
|
||
for (const fkey in cube.faces) {
|
||
const face = cube.faces[fkey];
|
||
const verticesIDs = [];
|
||
|
||
const corner_indices = face_corners[fkey]; // get the right 4 corner indices
|
||
if (!corner_indices) continue; // just in case
|
||
|
||
const face_vertices = corner_indices.map((i) => corners[i]);
|
||
|
||
for (const v of face_vertices) {
|
||
const position = getWorldPosition(v);
|
||
verticesIDs.push(addVertex(position));
|
||
}
|
||
|
||
if (verticesIDs.length == 4) {
|
||
addIndex(verticesIDs[0]);
|
||
addIndex(verticesIDs[1]);
|
||
addIndex(verticesIDs[2]);
|
||
|
||
addIndex(verticesIDs[1]);
|
||
addIndex(verticesIDs[3]);
|
||
addIndex(verticesIDs[2]);
|
||
}
|
||
|
||
// Calculate normal like in your mesh code
|
||
var origin = getWorldPosition(face_vertices[0]);
|
||
var dirBRef = getWorldPosition(face_vertices[1]);
|
||
var dirARef = getWorldPosition(face_vertices[2]);
|
||
var dirA = [0, 0, 0];
|
||
var dirB = [0, 0, 0];
|
||
for (var i = 0; i < 3; i++) {
|
||
dirA[i] = dirARef[i] - origin[i];
|
||
dirB[i] = dirBRef[i] - origin[i];
|
||
}
|
||
|
||
var normalDir = [0, 0, 0];
|
||
normalDir[0] = dirB[1] * dirA[2] - dirB[2] * dirA[1];
|
||
normalDir[1] = dirB[2] * dirA[0] - dirB[0] * dirA[2];
|
||
normalDir[2] = dirB[0] * dirA[1] - dirB[1] * dirA[0];
|
||
|
||
var normalMagnitude = Math.sqrt(
|
||
normalDir[0] * normalDir[0] +
|
||
normalDir[1] * normalDir[1] +
|
||
normalDir[2] * normalDir[2]
|
||
);
|
||
|
||
for (var i = 0; i < 3; i++) {
|
||
normalDir[i] = normalDir[i] / normalMagnitude;
|
||
}
|
||
|
||
for (var i = 0; i < verticesIDs.length; i++) {
|
||
addNormal(normalDir);
|
||
}
|
||
}
|
||
});
|
||
|
||
Blockbench.export({
|
||
type: "DeerMesh",
|
||
extensions: ["dmesh"],
|
||
name: Project.name,
|
||
content: autoStringify(json),
|
||
});
|
||
|
||
/*
|
||
Blockbench.showQuickMessage(
|
||
PathModule.join(folder, Project.name + ".dmesh"),
|
||
10000
|
||
);
|
||
Blockbench.writeFile(PathModule.join(folder, Project.name + ".dmesh"), {
|
||
content: autoStringify(json),
|
||
});*/
|
||
}
|
||
|
||
Plugin.register("deer_engine_exporter", {
|
||
title: "Deer Engine",
|
||
author: "Chewico",
|
||
description: "Utilities to work with Deer Engine",
|
||
icon: "export", // any valid Blockbench icon :contentReference[oaicite:5]{index=5}
|
||
version: "1.0.0",
|
||
variant: "both", // supports desktop & web
|
||
|
||
onload() {
|
||
// (1) Create the export action
|
||
exporterAction = new Action("export_deer_engine_json", {
|
||
name: "Export to Deer Engine Mesh Format",
|
||
icon: "export",
|
||
click() {
|
||
//visibleOverridesDialog.show();
|
||
exportToDeerEngine();
|
||
},
|
||
});
|
||
// (2) Add it under File → Export (top position)
|
||
MenuBar.addAction(exporterAction, "file.export.0"); // “export” submenu, index 0 :contentReference[oaicite:6]{index=6}
|
||
},
|
||
|
||
onunload() {
|
||
// Remove the action when plugin unloads
|
||
exporterAction.delete();
|
||
},
|
||
});
|