Skip to content

Instantly share code, notes, and snippets.

@leMaik
Last active August 29, 2023 21:41
Show Gist options
  • Save leMaik/c8d243a0835dfb51f2a37f8b11d0628b to your computer and use it in GitHub Desktop.
Save leMaik/c8d243a0835dfb51f2a37f8b11d0628b to your computer and use it in GitHub Desktop.
Generate Chunky models

This generator helps with generating Chunky models from JSON models.

It doesn't support rotation yet but handles UV mapping pretty well. It also doesn't generate full classes but just some snippets (models, textures) instead.

Usage

node generate-quads.js path-to-some-model.json > some-model.java

License

Please read the following license very carefully. By using this script, you agree to it.

        DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 
                    Version 2, December 2004 

 Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> 

 Everyone is permitted to copy and distribute verbatim or modified 
 copies of this license document, and changing it is allowed as long 
 as the name is changed. 

            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 

  0. You just DO WHAT THE FUCK YOU WANT TO.
const definition = require(process.argv[process.argv.length - 1]);
const faces = [];
const allQuads = [];
for (const element of definition.elements) {
const quads = [];
const [fx, fy, fz] = element.from;
const [tx, ty, tz] = element.to;
if (element.faces.up) {
const { uv, rotation } = element.faces.up;
if (rotation === 90) {
quads.push(`new Quad(
new Vector3(${fx} / 16.0, ${ty} / 16.0, ${fz} / 16.0),
new Vector3(${fx} / 16.0, ${ty} / 16.0, ${tz} / 16.0),
new Vector3(${tx} / 16.0, ${ty} / 16.0, ${fz} / 16.0),
${
uv
? `new Vector4(${uv[0]} / 16.0, ${uv[2]} / 16.0, ${16 - uv[3]} / 16.0, ${16 - uv[1]} / 16.0)`
: `new Vector4(${fx} / 16.0, ${tx} / 16.0, 1 - ${tz} / 16.0, 1 - ${fz} / 16.0)`
}
)`);
} else if (rotation === 180) {
quads.push(`new Quad(
new Vector3(${tx} / 16.0, ${ty} / 16.0, ${fz} / 16.0),
new Vector3(${fx} / 16.0, ${ty} / 16.0, ${fz} / 16.0),
new Vector3(${tx} / 16.0, ${ty} / 16.0, ${tz} / 16.0),
${
uv
? `new Vector4(${uv[0]} / 16.0, ${uv[2]} / 16.0, ${16 - uv[3]} / 16.0, ${16 - uv[1]} / 16.0)`
: `new Vector4(${fx} / 16.0, ${tx} / 16.0, 1 - ${tz} / 16.0, 1 - ${fz} / 16.0)`
}
)`);
} else if (rotation === 270) {
quads.push(`new Quad(
new Vector3(${tx} / 16.0, ${ty} / 16.0, ${tz} / 16.0),
new Vector3(${tx} / 16.0, ${ty} / 16.0, ${fz} / 16.0),
new Vector3(${fx} / 16.0, ${ty} / 16.0, ${tz} / 16.0),
${
uv
? `new Vector4(${uv[0]} / 16.0, ${uv[2]} / 16.0, ${16 - uv[3]} / 16.0, ${16 - uv[1]} / 16.0)`
: `new Vector4(${fx} / 16.0, ${tx} / 16.0, ${tz} / 16.0, ${fz} / 16.0)`
}
)`);
} else {
quads.push(`new Quad(
new Vector3(${fx} / 16.0, ${ty} / 16.0, ${tz} / 16.0),
new Vector3(${tx} / 16.0, ${ty} / 16.0, ${tz} / 16.0),
new Vector3(${fx} / 16.0, ${ty} / 16.0, ${fz} / 16.0),
${
uv
? `new Vector4(${uv[0]} / 16.0, ${uv[2]} / 16.0, ${16 - uv[3]} / 16.0, ${16 - uv[1]} / 16.0)`
: `new Vector4(${fx} / 16.0, ${tx} / 16.0, 1 - ${tz} / 16.0, 1 - ${fz} / 16.0)`
}
)`);
}
faces.push(element.faces.up);
}
if (element.faces.down) {
const { uv, rotation } = element.faces.down;
if (rotation === 90) {
// TODO this should probably rotate the other way around
quads.push(`new Quad(
new Vector3(${tx} / 16.0, ${fy} / 16.0, ${fz} / 16.0),
new Vector3(${tx} / 16.0, ${fy} / 16.0, ${tz} / 16.0),
new Vector3(${fx} / 16.0, ${fy} / 16.0, ${fz} / 16.0),
${
uv
? `new Vector4(${uv[0]} / 16.0, ${uv[2]} / 16.0, ${16 - uv[3]} / 16.0, ${16 - uv[1]} / 16.0)`
: `new Vector4(${fx} / 16.0, ${tx} / 16.0, ${tz} / 16.0, ${fz} / 16.0)`
}
)`);
}
if (rotation === 270) {
quads.push(`new Quad(
new Vector3(${tx} / 16.0, ${fy} / 16.0, ${fz} / 16.0),
new Vector3(${tx} / 16.0, ${fy} / 16.0, ${tz} / 16.0),
new Vector3(${fx} / 16.0, ${fy} / 16.0, ${fz} / 16.0),
${
uv
? `new Vector4(${uv[0]} / 16.0, ${uv[2]} / 16.0, ${16 - uv[3]} / 16.0, ${16 - uv[1]} / 16.0)`
: `new Vector4(${fx} / 16.0, ${tx} / 16.0, ${tz} / 16.0, ${fz} / 16.0)`
}
)`);
} else {
quads.push(`new Quad(
new Vector3(${fx} / 16.0, ${fy} / 16.0, ${fz} / 16.0),
new Vector3(${tx} / 16.0, ${fy} / 16.0, ${fz} / 16.0),
new Vector3(${fx} / 16.0, ${fy} / 16.0, ${tz} / 16.0),
${
uv
? `new Vector4(${uv[0]} / 16.0, ${uv[2]} / 16.0, ${16 - uv[3]} / 16.0, ${16 - uv[1]} / 16.0)`
: `new Vector4(${fx} / 16.0, ${tx} / 16.0, ${tz} / 16.0, ${fz} / 16.0)`
}
)`);
}
faces.push(element.faces.down);
}
if (element.faces.west) {
const { uv, rotation } = element.faces.west;
if (rotation === 90) {
quads.push(`new Quad(
new Vector3(${fx} / 16.0, ${fy} / 16.0, ${tz} / 16.0),
new Vector3(${fx} / 16.0, ${ty} / 16.0, ${tz} / 16.0),
new Vector3(${fx} / 16.0, ${fy} / 16.0, ${fz} / 16.0),
${
uv
? // ? `new Vector4(${16 - uv[0]} / 16.0, ${16 - uv[2]} / 16.0, ${16 -
// uv[1]} / 16.0, ${16 - uv[3]} / 16.0)`
// : `new Vector4(${fz} / 16.0, ${tz} / 16.0, ${fy} / 16.0, ${ty} / 16.0)`
`new Vector4(${uv[2]} / 16.0, ${uv[0]} / 16.0, ${16 - uv[1]} / 16.0, ${16 - uv[3]} / 16.0)`
: `new Vector4(${fz} / 16.0, ${tz} / 16.0, 1 - ${fy} / 16.0, 1 - ${ty} / 16.0)`
}
)`);
} else {
quads.push(`new Quad(
new Vector3(${fx} / 16.0, ${ty} / 16.0, ${tz} / 16.0),
new Vector3(${fx} / 16.0, ${ty} / 16.0, ${fz} / 16.0),
new Vector3(${fx} / 16.0, ${fy} / 16.0, ${tz} / 16.0),
${
uv
? // ? `new Vector4(${16 - uv[0]} / 16.0, ${16 - uv[2]} / 16.0, ${16 -
// uv[1]} / 16.0, ${16 - uv[3]} / 16.0)`
// : `new Vector4(${fz} / 16.0, ${tz} / 16.0, ${fy} / 16.0, ${ty} / 16.0)`
`new Vector4(${uv[2]} / 16.0, ${uv[0]} / 16.0, ${16 - uv[1]} / 16.0, ${16 - uv[3]} / 16.0)`
: `new Vector4(${tz} / 16.0, ${fz} / 16.0, ${ty} / 16.0, ${fy} / 16.0)`
}
)`);
}
faces.push(element.faces.west);
}
if (element.faces.east) {
const { uv, rotation } = element.faces.east;
if (rotation === 90) {
quads.push(`new Quad(
new Vector3(${tx} / 16.0, ${fy} / 16.0, ${fz} / 16.0),
new Vector3(${tx} / 16.0, ${ty} / 16.0, ${fz} / 16.0),
new Vector3(${tx} / 16.0, ${fy} / 16.0, ${tz} / 16.0),
${
uv
? // ? `new Vector4(${16 - uv[0]} / 16.0, ${16 - uv[2]} / 16.0, ${16 -
// uv[1]} / 16.0, ${16 - uv[3]} / 16.0)`
// : `new Vector4(${fz} / 16.0, ${tz} / 16.0, 1 - ${ty} / 16.0, 1 - ${fy} / 16.0)`
`new Vector4(${uv[2]} / 16.0, ${uv[0]} / 16.0, ${16 - uv[1]} / 16.0, ${16 - uv[3]} / 16.0)`
: `new Vector4(${fz} / 16.0, ${tz} / 16.0, 1 - ${ty} / 16.0, 1 - ${fy} / 16.0)`
}
)`);
} else {
quads.push(`new Quad(
new Vector3(${tx} / 16.0, ${ty} / 16.0, ${fz} / 16.0),
new Vector3(${tx} / 16.0, ${ty} / 16.0, ${tz} / 16.0),
new Vector3(${tx} / 16.0, ${fy} / 16.0, ${fz} / 16.0),
${
uv
? // ? `new Vector4(${16 - uv[0]} / 16.0, ${16 - uv[2]} / 16.0, ${16 -
// uv[1]} / 16.0, ${16 - uv[3]} / 16.0)`
// : `new Vector4(${fz} / 16.0, ${tz} / 16.0, 1 - ${ty} / 16.0, 1 - ${fy} / 16.0)`
`new Vector4(${uv[2]} / 16.0, ${uv[0]} / 16.0, ${16 - uv[1]} / 16.0, ${16 - uv[3]} / 16.0)`
: `new Vector4(${fz} / 16.0, ${tz} / 16.0, ${ty} / 16.0, ${fy} / 16.0)`
}
)`);
}
faces.push(element.faces.east);
}
if (element.faces.north) {
const { uv } = element.faces.north;
quads.push(`new Quad(
new Vector3(${fx} / 16.0, ${ty} / 16.0, ${fz} / 16.0),
new Vector3(${tx} / 16.0, ${ty} / 16.0, ${fz} / 16.0),
new Vector3(${fx} / 16.0, ${fy} / 16.0, ${fz} / 16.0),
${
uv
? `new Vector4(${uv[2]} / 16.0, ${uv[0]} / 16.0, ${16 - uv[1]} / 16.0, ${16 - uv[3]} / 16.0)`
: `new Vector4(1 - ${tx} / 16.0, 1 - ${fx} / 16.0, ${ty} / 16.0, ${fy} / 16.0)`
}
)`);
faces.push(element.faces.north);
}
if (element.faces.south) {
const { uv } = element.faces.south;
quads.push(`new Quad(
new Vector3(${tx} / 16.0, ${ty} / 16.0, ${tz} / 16.0),
new Vector3(${fx} / 16.0, ${ty} / 16.0, ${tz} / 16.0),
new Vector3(${tx} / 16.0, ${fy} / 16.0, ${tz} / 16.0),
${
uv
? `new Vector4(${uv[2]} / 16.0, ${uv[0]} / 16.0, ${16 - uv[1]} / 16.0, ${16 - uv[3]} / 16.0)`
: `new Vector4(${tx} / 16.0, ${fx} / 16.0, ${ty} / 16.0, ${fy} / 16.0)`
}
)`);
faces.push(element.faces.south);
}
if (element.rotation) {
if (element.rotation.axis === "y") {
if (element.rotation.pivot) {
// extension of the model syntax to simplify entity models, not used by Minecraft or for blocks
allQuads.push(`Model.transform(
new Quad[]{
${quads.join(",\n")}
},
Transform.NONE
.translate(0.5, 0.5, 0.5)
.rotateY(Math.toRadians(${element.rotation.angle}))
.translate(-0.5, -0.5, -0.5)
.translate(${element.rotation.pivot[0]} / 16.0, ${element.rotation.pivot[1]} / 16.0, ${
element.rotation.pivot[2]
} / 16.0)
)`);
} else {
if (
element.rotation.angle === 90 &&
(element.rotation.origin?.[0] ?? 8) === 8 &&
(element.rotation.origin?.[2] ?? 8) === 8
) {
allQuads.push(`Model.rotateY(
new Quad[]{
${quads.join(",\n")}
}
)`);
} else if (
element.rotation.angle === -90 &&
(element.rotation.origin?.[0] ?? 8) === 8 &&
(element.rotation.origin?.[2] ?? 8) === 8
) {
allQuads.push(`Model.rotateNegY(
new Quad[]{
${quads.join(",\n")}
}
)`);
} else if ((element.rotation.origin?.[0] ?? 8) === 8 && (element.rotation.origin?.[2] ?? 8) === 8) {
allQuads.push(`Model.rotateY(
new Quad[]{
${quads.join(",\n")}
},
Math.toRadians(${element.rotation.angle})
)`);
} else {
allQuads.push(`Model.rotateY(
new Quad[]{
${quads.join(",\n")}
},
Math.toRadians(${element.rotation.angle}),
new Vector3(${element.rotation.origin[0]}, 0, ${element.rotation.origin[2]})
)`);
}
}
}
// TODO x and z axis rotation
} else {
allQuads.push(quads);
}
}
const usedTextures = Object.entries(definition.textures || {}).filter(([texture]) =>
faces.some((face) => face.texture === `#${texture}`)
);
console.log(`
${usedTextures
.map(([texture, path]) => `private static final Texture ${texture} = Texture.${snakeToCamel(path.split("/")[1])};`)
.join("\n")}
private static final Texture[] tex = new Texture[]{
${faces.map((face) => face.texture.substr(1)).join(", ")}
};
`);
if (allQuads.some((quads) => typeof quads === "string")) {
console.log(`
private static final Quad[] quads = Model.join(
${allQuads
.flatMap((quads) =>
typeof quads === "string"
? quads
: `new Quad[]{
${quads.join(",\n")}
}`
)
.join(",\n")}
);
`);
} else {
console.log(`
private static final Quad[] quads = new Quad[]{
${allQuads.flat().join(",\n")}
};
`);
}
console.log(
usedTextures
.map(
([texture, path]) =>
`@TexturePath("assets/minecraft/textures/${path}")\npublic static final Texture ${snakeToCamel(
path.split("/")[1]
)} = new Texture();`
)
.join("\n")
);
function snakeToCamel(str) {
return str.replace(/(_[a-z])/g, (group) => group.toUpperCase().replace("_", ""));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment