oma-tank/data/models.lua
2026-04-18 11:35:06 +01:00

231 lines
9 KiB
Lua
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- Wireframe 3D models — vertex + edge data traced from the public 1980 arcade
-- AVG ROM (vertex table at $388e, draw-command table at $2472, both
-- documented by the 6502 disassembly preservation project).
--
-- Axis translation: the arcade uses +X forward, +Y right, +Z up. We use
-- +X right, +Y up, +Z forward. The arcade math box effectively halves X/Z
-- at draw time (visualizer's X and Z, which are arcade's right-axis and
-- forward-axis), so those are stored 2× and we halve here. Arcade's Z (up)
-- axis is stored at native scale.
--
-- All models pre-scaled ×0.025 to fit our world; bottoms shifted to Y=0
-- except the shell, which is centred so it can fly at arbitrary Y.
local Models = {}
local function buildModel(verts, lines, scale)
scale = scale or 1
local edges = {}
for _, pair in ipairs(lines) do
local a, b = verts[pair[1]], verts[pair[2]]
edges[#edges + 1] = {
a[1] * scale, a[2] * scale, a[3] * scale,
b[1] * scale, b[2] * scale, b[3] * scale,
}
end
return edges
end
-- Slow (enemy) tank — arcade ROM shape 2 — 24v, 38e.
-- Stepped hull, moderate height, forward-extending turret / antenna.
local tankVerts = {
{ 6.40, 0.00, -9.20}, { -6.40, 0.00, -9.20},
{ -6.40, 0.00, 12.10}, { 6.40, 0.00, 12.10},
{ 7.10, 2.80, -12.80}, { -7.10, 2.80, -12.80},
{ -7.10, 2.80, 15.60}, { 7.10, 2.80, 15.60},
{ 4.30, 5.00, -8.50}, { -4.30, 5.00, -8.50},
{ -4.30, 5.00, 8.50}, { 4.30, 5.00, 8.50},
{ 2.10, 9.20, -6.40}, { -2.10, 9.20, -6.40},
{ 0.50, 7.80, -1.60}, { -0.50, 7.80, -1.60},
{ -0.50, 6.80, 1.60}, { 0.50, 6.80, 1.60},
{ -0.50, 7.80, 14.00}, { -0.50, 6.80, 14.00},
{ 0.50, 7.80, 14.00}, { 0.50, 6.80, 14.00},
{ 0.00, 9.20, -6.40}, { 0.00, 10.00, -6.40},
}
local tankLines = {
{24,23},{13,14},{15,21},{21,19},{19,16},{16,15},{15,18},{18,17},
{17,20},{20,22},{22,18},{16,17},{20,19},{21,22},{4,1},{1,5},
{5,8},{8,7},{7,3},{3,4},{4,8},{8,12},{12,11},{11,7},
{7,6},{6,10},{10,11},{11,14},{14,10},{10,9},{9,12},{12,13},
{13,9},{9,5},{5,6},{6,2},{2,3},{2,1},
}
Models.tank = buildModel(tankVerts, tankLines)
-- Super tank — arcade ROM shape 33 — 25v, 34e.
-- Similar stepped profile but slightly taller and with rear antenna mount.
local supertankVerts = {
{ -4.60, 0.00, 18.20}, { -6.90, 0.00, -5.70},
{ 6.90, 0.00, -5.70}, { 4.60, 0.00, 18.20},
{ -5.70, 5.70, -5.70}, { 5.70, 5.70, -5.70},
{ 0.00, 1.10, 13.70}, { -3.40, 5.10, -3.40},
{ -3.40, 5.70, -5.70}, { 3.40, 5.70, -5.70},
{ 3.40, 5.10, -3.40}, { -2.30, 9.10, -3.40},
{ -2.30, 9.10, -5.70}, { 2.30, 9.10, -5.70},
{ 2.30, 9.10, -3.40}, { -1.10, 6.90, 16.00},
{ -1.10, 6.90, 1.10}, { 1.10, 6.90, 1.10},
{ 1.10, 6.90, 16.00}, { -1.10, 8.00, 16.00},
{ -1.10, 8.00, -1.10}, { 1.10, 8.00, -1.10},
{ 1.10, 8.00, 16.00}, { 0.00, 9.10, -5.70},
{ 0.00, 14.90, -5.70},
}
local supertankLines = {
{1,2},{2,5},{5,1},{1,4},{4,3},{3,6},{6,4},{3,2},
{5,6},{10,11},{11,7},{7,15},{15,14},{14,10},{10,9},{9,8},
{8,7},{7,12},{12,13},{13,9},{13,14},{15,12},{20,23},{23,22},
{22,21},{21,17},{17,16},{16,19},{19,18},{18,17},{16,20},{23,19},
{18,22},{24,25},
}
Models.supertank = buildModel(supertankVerts, supertankLines)
-- Guided missile — arcade ROM shape 22 (type $16) — 26v, 43e.
local missileVerts = {
{ -1.80, 4.20, -4.80}, { -0.90, 5.40, -4.80},
{ 0.90, 5.40, -4.80}, { 1.80, 4.20, -4.80},
{ 0.90, 3.00, -4.80}, { -0.90, 3.00, -4.80},
{ -3.60, 4.20, -1.20}, { -2.40, 6.60, -1.20},
{ 2.40, 6.60, -1.20}, { 3.60, 4.20, -1.20},
{ 2.40, 1.80, -1.20}, { -2.40, 1.80, -1.20},
{ 0.00, 4.20, 14.40}, { 0.00, 4.20, 17.40},
{ 1.80, 0.00, -1.80}, { -1.80, 0.00, -1.80},
{ -1.80, 0.00, 1.80}, { 1.80, 0.00, 1.80},
{ 0.60, 1.90, -0.60}, { -0.60, 1.90, -0.60},
{ -0.60, 2.10, 0.60}, { 0.60, 2.10, 0.60},
{ 0.00, 6.60, -1.20}, { -0.90, 5.40, 6.60},
{ 0.90, 5.40, 6.60}, { 0.00, 7.80, 0.60},
}
local missileLines = {
{14,13},{13,7},{7,1},{1,2},{2,8},{8,9},{9,10},{10,11},
{11,12},{12,7},{7,8},{8,13},{13,9},{9,3},{3,4},{4,10},
{10,13},{13,11},{11,5},{5,6},{6,12},{12,13},{25,24},{24,23},
{23,25},{25,26},{26,24},{26,23},{2,3},{4,5},{6,1},{19,20},
{20,21},{21,22},{22,19},{19,15},{15,16},{16,17},{17,18},{18,15},
{16,20},{21,17},{18,22},
}
Models.missile = buildModel(missileVerts, missileLines)
-- Saucer / UFO — arcade ROM shape 32 (type $20) — 17v, 32e.
local saucerVerts = {
{ 0.00, 0.00, -3.00}, { -2.00, 0.00, -2.00},
{ -3.00, 0.00, 0.00}, { -2.00, 0.00, 2.00},
{ 0.00, 0.00, 3.00}, { 2.00, 0.00, 2.00},
{ 3.00, 0.00, 0.00}, { 2.00, 0.00, -2.00},
{ 0.00, 3.00, -12.00}, { -8.50, 3.00, -8.50},
{ -12.00, 3.00, 0.00}, { -8.50, 3.00, 8.50},
{ 0.00, 3.00, 12.00}, { 8.50, 3.00, 8.50},
{ 12.00, 3.00, 0.00}, { 8.50, 3.00, -8.50},
{ 0.00, 8.00, 0.00},
}
local saucerLines = {
{17,9},{9,10},{10,17},{17,11},{11,12},{12,17},{17,13},{13,14},
{14,17},{17,15},{15,16},{16,17},{1,8},{8,16},{16,9},{9,1},
{1,2},{2,10},{10,11},{11,3},{3,4},{4,12},{12,13},{13,5},
{5,6},{6,14},{14,15},{15,7},{7,8},{7,6},{5,4},{3,2},
}
Models.ufo = buildModel(saucerVerts, saucerLines)
-- Low/wide pyramid obstacle — arcade ROM shape 12 — 5v, 8e.
local pyramidVerts = {
{ 10.00, 0.00, -10.00}, { -10.00, 0.00, -10.00},
{ -10.00, 0.00, 10.00}, { 10.00, 0.00, 10.00},
{ 0.00, 18.00, 0.00},
}
local pyramidLines = {
{1,5},{5,2},{2,1},{1,4},{4,5},{5,3},{3,4},{3,2},
}
Models.pyramid = buildModel(pyramidVerts, pyramidLines)
-- Tall / narrower pyramid obstacle — arcade ROM shape 0 — 5v, 8e.
local pyramidTallVerts = {
{ 6.40, 0.00, -6.40}, { -6.40, 0.00, -6.40},
{ -6.40, 0.00, 6.40}, { 6.40, 0.00, 6.40},
{ 0.00, 16.00, 0.00},
}
local pyramidTallLines = {
{1,5},{5,2},{2,1},{1,4},{4,5},{5,3},{3,4},{3,2},
}
Models.pyramidTall = buildModel(pyramidTallVerts, pyramidTallLines)
-- Tall block / cube — arcade ROM shape 1 — 8v, 12e.
-- Taller than wide — matches arcade's "tall box" obstacle.
local cubeVerts = {
{ 6.40, 0.00, -6.40}, { -6.40, 0.00, -6.40},
{ -6.40, 0.00, 6.40}, { 6.40, 0.00, 6.40},
{ 6.40, 16.00, -6.40}, { -6.40, 16.00, -6.40},
{ -6.40, 16.00, 6.40}, { 6.40, 16.00, 6.40},
}
local cubeLines = {
{1,2},{2,3},{3,4},{4,1},{1,5},{5,6},{6,7},{7,8},
{8,5},{6,2},{3,7},{8,4},
}
Models.cube = buildModel(cubeVerts, cubeLines)
-- Low block / slab — arcade ROM shape 15 — 8v, 12e.
local halfPrismVerts = {
{ 8.00, 0.00, -8.00}, { -8.00, 0.00, -8.00},
{ -8.00, 0.00, 8.00}, { 8.00, 0.00, 8.00},
{ 8.00, 7.00, -8.00}, { -8.00, 7.00, -8.00},
{ -8.00, 7.00, 8.00}, { 8.00, 7.00, 8.00},
}
local halfPrismLines = {
{1,2},{2,3},{3,4},{4,1},{1,5},{5,6},{6,7},{7,8},
{8,5},{6,2},{3,7},{8,4},
}
Models.halfPrism = buildModel(halfPrismVerts, halfPrismLines)
-- Tank shell — the arcade draws in-flight shells as a single bright point,
-- so there's no ROM model to trace. We use a small square-base pyramid with
-- the apex pointing forward (+Z) so the player can actually see their shot.
local shellVerts = {
{ 2.0, 1.5, 0.0}, { 2.0, -1.5, 0.0},
{ -2.0, -1.5, 0.0}, { -2.0, 1.5, 0.0},
{ 0.0, 0.0, 8.0},
}
local shellLines = {
{1,2},{2,3},{3,4},{4,1},
{1,5},{2,5},{3,5},{4,5},
}
Models.shell = buildModel(shellVerts, shellLines)
-- Debris — three shrapnel shapes used by the tank-destruction explosion.
local debris1Verts = {
{ 0, -3.9, 0}, { 0, 0, 3}, { 0, 3, -0.75}, { 0, 1.5, -2.1},
{ 3, 0, 0}, {-3, 0, -0.3},
}
local debris1Lines = {
{1,2},{2,3},{3,4},{4,1},
{1,5},{2,5},{3,5},{4,5},
{1,6},{2,6},{3,6},{4,6},
}
Models.debris1 = buildModel(debris1Verts, debris1Lines)
local debris2Verts = {
{ 0, -3, 9}, { 2.1, -3, 7.5}, { 1.5, -3, 0}, { 0.6, -3, -0.75},
{-1.2, -3, 6}, { 0, 0, 1.5}, { 0, 4.5, 0},
}
local debris2Lines = {
{1,2},{2,3},{3,4},{4,5},{5,1},
{1,6},{6,7},{7,4},{7,3},
}
Models.debris2 = buildModel(debris2Verts, debris2Lines)
local debris3Verts = {
{ 0, 3, 12}, { 0, 3, -3},
{ 1.5, 0, 11.1}, { 1.5, 0, 0}, { 0, 0, -3}, {-1.5, 0, 0}, {-1.5, 0, 10.5},
{ 0, -3, 9}, { 0, -3, 0},
{ 0, -6, 0}, { 1.5, -6, 0}, { 0, -6, -3}, {-1.5, -6, 0},
}
local debris3Lines = {
{1,2},
{3,4},{4,5},{5,6},{6,7},
{8,9},
{1,8},{1,3},{1,7},{3,8},{7,8},
{9,10},
{10,11},{11,12},{12,13},{13,10},
{11,4},{13,6},{12,5},
{4,9},{6,9},
{2,5},{2,4},{2,6},
}
Models.debris3 = buildModel(debris3Verts, debris3Lines)
Models.debrisPool = { Models.debris1, Models.debris2, Models.debris3 }
return Models