Skip to content

Commit 954aa09

Browse files
committed
Initial commit
1 parent 310f40c commit 954aa09

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1322
-0
lines changed

bmd.js

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
(function(exports) {
2+
"use strict";
3+
4+
// SM64 .bmd format
5+
6+
function readString(buffer, offs, length) {
7+
var buf = new Uint8Array(buffer, offs, length);
8+
var S = '';
9+
for (var i = 0; i < length; i++) {
10+
if (buf[i] === 0)
11+
break;
12+
S += String.fromCharCode(buf[i]);
13+
}
14+
return S;
15+
}
16+
17+
function parseModel(bmd, view, idx) {
18+
var offs = bmd.modelOffsBase + idx * 0x40;
19+
20+
var model = {};
21+
model.id = view.getUint32(offs + 0x00, true);
22+
model.name = readString(view.buffer, view.getUint32(offs + 0x04, true), 0xFF);
23+
24+
model.parentID = view.getUint16(offs + 0x08, true);
25+
26+
// Local transform.
27+
var xs = view.getUint32(offs + 0x10, true);
28+
var ys = view.getUint32(offs + 0x14, true);
29+
var zs = view.getUint32(offs + 0x18, true);
30+
var xr = view.getUint16(offs + 0x1C, true);
31+
var yr = view.getUint16(offs + 0x1E, true);
32+
var zr = view.getUint16(offs + 0x20, true);
33+
var xt = view.getUint16(offs + 0x24, true);
34+
var yt = view.getUint16(offs + 0x28, true);
35+
var zt = view.getUint16(offs + 0x2C, true);
36+
37+
// A "batch" is a combination of a material and a poly.
38+
var batchCount = view.getUint32(offs + 0x30, true);
39+
var batchMaterialOffs = view.getUint32(offs + 0x34, true);
40+
var batchPolyOffs = view.getUint32(offs + 0x38, true);
41+
42+
model.batches = [];
43+
44+
for (var i = 0; i < batchCount; i++) {
45+
var materialIdx = view.getUint8(batchMaterialOffs + i);
46+
var material = parseMaterial(bmd, view, materialIdx);
47+
var baseCtx = { s_color: material.diffuse, alpha: material.alpha };
48+
49+
var polyIdx = view.getUint8(batchPolyOffs + i);
50+
var poly = parsePoly(bmd, view, polyIdx, baseCtx);
51+
52+
model.batches.push({ material: material, poly: poly });
53+
}
54+
55+
return model;
56+
}
57+
58+
function parsePoly(bmd, view, idx, baseCtx) {
59+
var offs = view.getUint32((bmd.polyOffsBase + idx * 0x08) + 0x04, true);
60+
61+
var gxCmdSize = view.getUint32(offs + 0x08, true);
62+
var gxCmdOffs = view.getUint32(offs + 0x0C, true);
63+
64+
var gxCmdBuf = view.buffer.slice(gxCmdOffs, gxCmdOffs + gxCmdSize);
65+
66+
return { packets: NITRO_GX.readCmds(gxCmdBuf, baseCtx) };
67+
}
68+
69+
function parseMaterial(bmd, view, idx) {
70+
var offs = bmd.materialOffsBase + idx * 0x30;
71+
72+
var material = {};
73+
material.name = readString(view.buffer, view.getUint32(offs + 0x00, true), 0xFF);
74+
material.texCoordMat = mat4.create();
75+
76+
var textureIdx = view.getUint32(offs + 0x04, true);
77+
if (textureIdx !== 0xFFFFFFFF) {
78+
var paletteIdx = view.getUint32(offs + 0x08, true);
79+
material.texture = parseTexture(bmd, view, textureIdx, paletteIdx);
80+
material.texParams = material.texture.params | view.getUint32(offs + 0x20, true);
81+
82+
if (material.texParams >> 30) {
83+
var scaleS = view.getInt32(offs + 0x0C, true) / 4096.0;
84+
var scaleT = view.getInt32(offs + 0x10, true) / 4096.0;
85+
var transS = view.getInt32(offs + 0x18, true) / 4096.0;
86+
var transT = view.getInt32(offs + 0x1C, true) / 4096.0;
87+
mat4.translate(material.texCoordMat, material.texCoordMat, [transS, transT, 0.0]);
88+
mat4.scale(material.texCoordMat, material.texCoordMat, [scaleS, scaleT, 1.0]);
89+
}
90+
mat4.scale(material.texCoordMat, material.texCoordMat, [1/material.texture.width, 1/material.texture.height, 1]);
91+
} else {
92+
material.texture = null;
93+
material.texParams = 0;
94+
}
95+
96+
var polyAttribs = view.getUint32(offs + 0x24, true);
97+
var alpha = (polyAttribs >> 16) & 0x1F;
98+
alpha = (alpha << (8-5)) | (alpha >>> (10-8));
99+
100+
// NITRO's Rendering Engine uses two passes. Opaque, then Transparent.
101+
// A transparent polygon is one that has an alpha of < 0xFF, or uses
102+
// A5I3 / A3I5 textures.
103+
104+
material.isTranslucent = (alpha < 0xFF) || (material.texture && material.texture.isTranslucent);
105+
106+
// Do transparent polys write to the depth buffer?
107+
var xl = (polyAttribs >>> 1) & 0x01;
108+
if (xl)
109+
material.depthWrite = true;
110+
else
111+
material.depthWrite = !material.isTranslucent;
112+
113+
var difAmb = view.getUint32(offs + 0x28, true);
114+
if (difAmb & 0x8000)
115+
material.diffuse = NITRO_GX.rgb5(difAmb & 0x07FF);
116+
else
117+
material.diffuse = [0xFF, 0xFF, 0xFF];
118+
119+
material.alpha = alpha;
120+
121+
return material;
122+
}
123+
124+
function textureToCanvas(texture) {
125+
var canvas = document.createElement("canvas");
126+
canvas.width = texture.width;
127+
canvas.height = texture.height;
128+
129+
var ctx = canvas.getContext("2d");
130+
var imgData = ctx.createImageData(canvas.width, canvas.height);
131+
132+
for (var i = 0; i < imgData.data.length; i++)
133+
imgData.data[i] = texture.pixels[i];
134+
135+
canvas.title = texture.name;
136+
137+
ctx.putImageData(imgData, 0, 0);
138+
return canvas;
139+
}
140+
141+
function parseTexture(bmd, view, texIdx, palIdx) {
142+
var texOffs = bmd.textureOffsBase + texIdx * 0x14;
143+
144+
var texture = {};
145+
texture.id = texIdx;
146+
texture.name = readString(view.buffer, view.getUint32(texOffs + 0x00, true), 0xFF);
147+
148+
var texDataOffs = view.getUint32(texOffs + 0x04, true);
149+
var texDataSize = view.getUint32(texOffs + 0x08, true);
150+
var texData = view.buffer.slice(texDataOffs);
151+
152+
texture.params = view.getUint32(texOffs + 0x10, true);
153+
texture.format = (texture.params >> 26) & 0x07;
154+
texture.width = 8 << ((texture.params >> 20) & 0x07);
155+
texture.height = 8 << ((texture.params >> 23) & 0x07);
156+
var color0 = (texture.params >> 29) & 0x01;
157+
158+
var palData = null;
159+
if (palIdx != 0xFFFFFFFF) {
160+
var palOffs = bmd.paletteOffsBase + palIdx * 0x10;
161+
texture.paletteName = readString(view.buffer, view.getUint32(palOffs + 0x00, true), 0xFF);
162+
var palDataOffs = view.getUint32(palOffs + 0x04, true);
163+
var palDataSize = view.getUint32(palOffs + 0x08, true);
164+
palData = view.buffer.slice(palDataOffs, palDataOffs + palDataSize);
165+
}
166+
167+
texture.pixels = NITRO_Tex.readTexture(texture.format, texture.width, texture.height, texData, palData, color0);
168+
169+
if (texture.pixels)
170+
document.querySelector('#textures').appendChild(textureToCanvas(texture));
171+
172+
texture.isTranslucent = (texture.format === NITRO_Tex.Format.Tex_A5I3 ||
173+
texture.format === NITRO_Tex.Format.Tex_A3I5);
174+
175+
return texture;
176+
}
177+
178+
var BMD = {};
179+
BMD.parse = function(buffer) {
180+
var view = new DataView(buffer);
181+
182+
var bmd = {};
183+
184+
bmd.scaleFactor = (1 << view.getUint32(0x00, true));
185+
186+
bmd.modelCount = view.getUint32(0x04, true);
187+
bmd.modelOffsBase = view.getUint32(0x08, true);
188+
bmd.polyCount = view.getUint32(0x0C, true);
189+
bmd.polyOffsBase = view.getUint32(0x10, true);
190+
bmd.textureCount = view.getUint32(0x14, true);
191+
bmd.textureOffsBase = view.getUint32(0x18, true);
192+
bmd.paletteCount = view.getUint32(0x1C, true);
193+
bmd.paletteOffsBase = view.getUint32(0x20, true);
194+
bmd.materialCount = view.getUint32(0x24, true);
195+
bmd.materialOffsBase = view.getUint32(0x28, true);
196+
197+
bmd.models = [];
198+
for (var i = 0; i < bmd.modelCount; i++)
199+
bmd.models.push(parseModel(bmd, view, i));
200+
201+
return bmd;
202+
};
203+
exports.BMD = BMD;
204+
205+
})(window);

exk/battan_king_map_all.bmd

33.7 KB
Binary file not shown.

exk/bombhei_map_all.bmd

35.1 KB
Binary file not shown.

exk/castle_1f_all.bmd

67 KB
Binary file not shown.

exk/castle_2f_all.bmd

61.4 KB
Binary file not shown.

exk/castle_b1_all.bmd

32 KB
Binary file not shown.

exk/cave_all.bmd

70.8 KB
Binary file not shown.

exk/clock_tower_all.bmd

40.7 KB
Binary file not shown.

exk/desert_land_all.bmd

35.2 KB
Binary file not shown.

exk/desert_py_all.bmd

29.7 KB
Binary file not shown.

0 commit comments

Comments
 (0)