examples: add cube

This commit is contained in:
Zack Buhman 2024-09-03 22:42:12 -05:00
parent fb624b986b
commit df31f45fd1
11 changed files with 536 additions and 4 deletions

View File

@ -18,6 +18,7 @@ DEFAULT = header.o arm7/arm7.bin.o
triangle.elf: $(DEFAULT) arm9/triangle.bin.o
triangle_rotating.elf: $(DEFAULT) arm9/triangle_rotating.bin.o
texture.elf: $(DEFAULT) arm9/texture.bin.o
cube.elf: $(DEFAULT) arm9/cube.bin.o
TARGET = arm-none-eabi-
AARCH = -march=armv4t -mlittle-endian

View File

@ -22,7 +22,9 @@ triangle_rotating.elf: start.o examples/triangle_rotating.o ../math/cos_table_fp
texture.elf: start.o examples/texture.o ../res/hotaru_futaba.data.o ../res/hotaru_futaba.data.pal.o
CFLAGS += -I../include -I../math
cube.elf: start.o examples/cube.o ../math/cos_table_fp12.o ../math/cos.o
CFLAGS += -I../include -I../
include arm9.mk
include ../common.mk

193
arm9/examples/cube.c Normal file
View File

@ -0,0 +1,193 @@
#include "io_registers.h"
#include "bits.h"
#include "models/cube.h"
#include "math/math.h"
static const uint16_t face_colors[6] = {
COLOR__blue(31),
COLOR__red(31),
COLOR__green(31),
COLOR__red(31) | COLOR__green(31),
COLOR__red(31) | COLOR__blue(31),
COLOR__green(31) | COLOR__blue(31),
};
void main()
{
// power control
io_registers.a.POWCNT = 0
| POWCNT__lcd_output_destination__a_to_upper__b_to_lower
| POWCNT__geometry_engine__enable
| POWCNT__rendering_engine__enable
| POWCNT__lcd__enable;
// enable bg0 and 3d graphics
io_registers.a.DISPCNT = 0
| DISPCNT__display_mode__graphics_display
| DISPCNT__bg0__enable
| DISPCNT__display_selection_for_bg0__3d_graphics
;
// disable all 3d effects
io_registers.a.DISP3DCNT = 0
| DISP3DCNT__clear_image__disable
| DISP3DCNT__fog_master__disable
| DISP3DCNT__edge_marking__disable
| DISP3DCNT__anti_aliasing__disable
| DISP3DCNT__alpha_blending__disable
| DISP3DCNT__alpha_test__disable
| DISP3DCNT__texture_mapping__disable;
// clear matrix stack status
io_registers.a.GXSTAT |= GXSTAT__matrix_stack_status__overflow_or_underflow;
// load identity matrices
io_registers.a.MTX_MODE = MTX_MODE__matrix_mode__projection;
io_registers.a.MTX_IDENTITY = 0;
// scale everything by 1/2
io_registers.a.MTX_SCALE = (1 << 12) / 2;
io_registers.a.MTX_SCALE = (1 << 12) / 2;
io_registers.a.MTX_SCALE = (1 << 12) / 2;
// scale the x-axis by the ratio of the display height by the display width.
io_registers.a.MTX_SCALE = (192 << 12) / 256;
io_registers.a.MTX_SCALE = 1 << 12;
io_registers.a.MTX_SCALE = 1 << 12;
io_registers.a.MTX_MODE = MTX_MODE__matrix_mode__position;
io_registers.a.MTX_IDENTITY = 0;
io_registers.a.MTX_MODE = MTX_MODE__matrix_mode__position_and_vector;
io_registers.a.MTX_IDENTITY = 0;
// set the 3d clear color to a dark red
io_registers.a.CLEAR_COLOR = 0
| CLEAR_COLOR__clear_polygon_id(31)
| CLEAR_COLOR__alpha_value(31)
| CLEAR_COLOR__blue(1)
| CLEAR_COLOR__green(1)
| CLEAR_COLOR__red(10);
// set the depth buffer clear value to the maximum value
io_registers.a.CLEAR_DEPTH = CLEAR_DEPTH__value(0x7fff);
// the following polygons are fully opaque and are not
// backface-culled
io_registers.a.POLYGON_ATTR = 0
| POLYGON_ATTR__alpha_value(31)
| POLYGON_ATTR__render_front_surface__enable
| POLYGON_ATTR__render_back_surface__enable;
// the 3d viewport is the entire display area
io_registers.a.VIEWPORT = 0
| VIEWPORT__y2(191)
| VIEWPORT__x2(255)
| VIEWPORT__y1(0)
| VIEWPORT__x1(0);
// degrees
int theta = 0;
while (1) {
// calculate sin/cos for 2d rotation; signed fp20.12 result
int cos = cos_fp12(theta);
int sin = sin_fp12(theta);
int cos2 = cos_fp12(theta >> 1);
int sin2 = sin_fp12(theta >> 1);
io_registers.a.MTX_MODE = MTX_MODE__matrix_mode__position;
// reset position matrix
io_registers.a.MTX_IDENTITY = 0;
// multiply by a z-axis rotation
io_registers.a.MTX_MULT_3X3 = cos;
io_registers.a.MTX_MULT_3X3 = -sin;
io_registers.a.MTX_MULT_3X3 = 0;
io_registers.a.MTX_MULT_3X3 = sin;
io_registers.a.MTX_MULT_3X3 = cos;
io_registers.a.MTX_MULT_3X3 = 0;
io_registers.a.MTX_MULT_3X3 = 0;
io_registers.a.MTX_MULT_3X3 = 0;
io_registers.a.MTX_MULT_3X3 = 1 << 12;
// multiply by a y-axis rotation
io_registers.a.MTX_MULT_3X3 = cos2;
io_registers.a.MTX_MULT_3X3 = 0;
io_registers.a.MTX_MULT_3X3 = sin2;
io_registers.a.MTX_MULT_3X3 = 0;
io_registers.a.MTX_MULT_3X3 = 1 << 12;
io_registers.a.MTX_MULT_3X3 = 0;
io_registers.a.MTX_MULT_3X3 = -sin2;
io_registers.a.MTX_MULT_3X3 = 0;
io_registers.a.MTX_MULT_3X3 = cos2;
// multiply by a x-axis rotation
io_registers.a.MTX_MULT_3X3 = cos2;
io_registers.a.MTX_MULT_3X3 = -sin2;
io_registers.a.MTX_MULT_3X3 = 0;
io_registers.a.MTX_MULT_3X3 = sin2;
io_registers.a.MTX_MULT_3X3 = cos2;
io_registers.a.MTX_MULT_3X3 = 0;
io_registers.a.MTX_MULT_3X3 = 0;
io_registers.a.MTX_MULT_3X3 = 0;
io_registers.a.MTX_MULT_3X3 = 1 << 12;
// the following vertices are a quadrilateral
io_registers.a.BEGIN_VTXS = BEGIN_VTXS__type__quadrilateral;
// cube faces
for (int i = 0; i < 6; i++) {
io_registers.a.COLOR = face_colors[i];
struct vertex_position * a = &cube_positions[cube_faces[i].a.position];
io_registers.a.VTX_10 = 0
| VTX_10__z_coordinate(a->z)
| VTX_10__y_coordinate(a->y)
| VTX_10__x_coordinate(a->x);
struct vertex_position * b = &cube_positions[cube_faces[i].b.position];
io_registers.a.VTX_10 = 0
| VTX_10__z_coordinate(b->z)
| VTX_10__y_coordinate(b->y)
| VTX_10__x_coordinate(b->x);
struct vertex_position * c = &cube_positions[cube_faces[i].c.position];
io_registers.a.VTX_10 = 0
| VTX_10__z_coordinate(c->z)
| VTX_10__y_coordinate(c->y)
| VTX_10__x_coordinate(c->x);
struct vertex_position * d = &cube_positions[cube_faces[i].d.position];
io_registers.a.VTX_10 = 0
| VTX_10__z_coordinate(d->z)
| VTX_10__y_coordinate(d->y)
| VTX_10__x_coordinate(d->x);
}
// end of the quadrilateral
io_registers.a.END_VTXS = 0;
// wait for the end of the current frame
while (io_registers.a.VCOUNT != 262);
while (io_registers.a.VCOUNT == 262);
// wait for the geometry engine
while (io_registers.a.GXSTAT & GXSTAT__geometry_engine_busy);
// swap buffers
io_registers.a.SWAP_BUFFERS = 0;
// increment theta once per frame
theta += 1;
if (theta >= 360 * 2) {
theta = 0;
}
}
}

View File

@ -1,8 +1,8 @@
#include "io_registers.h"
#include "bits.h"
#include "../res/hotaru_futaba.data.h"
#include "../res/hotaru_futaba.data.pal.h"
#include "res/hotaru_futaba.data.h"
#include "res/hotaru_futaba.data.pal.h"
static inline uint16_t rgb565(const uint8_t * buf)
{

View File

@ -1,7 +1,7 @@
#include "io_registers.h"
#include "bits.h"
#include "math.h"
#include "math/math.h"
void main()
{

71
models/cube.h Normal file
View File

@ -0,0 +1,71 @@
#include "model.h"
// .6 fixed-point
struct vertex_position cube_positions[] = {
{64, 64, -64},
{64, -64, -64},
{64, 64, 64},
{64, -64, 64},
{-64, 64, -64},
{-64, -64, -64},
{-64, 64, 64},
{-64, -64, 64},
};
// .15 fixed-point
struct vertex_texture cube_textures[] = {
{32768, 32768},
{0, 32768},
{0, 0},
{32768, 0},
};
// .9 fixed-point
struct vertex_normal cube_normals[] = {
{0, 512, 0},
{0, 0, 512},
{-512, 0, 0},
{0, -512, 0},
{512, 0, 0},
{0, 0, -512},
};
struct face cube_faces[] = {
{
{0, 0, 0},
{4, 1, 0},
{6, 2, 0},
{2, 3, 0},
},
{
{3, 3, 1},
{2, 0, 1},
{6, 1, 1},
{7, 2, 1},
},
{
{7, 2, 2},
{6, 1, 2},
{4, 0, 2},
{5, 3, 2},
},
{
{5, 1, 3},
{1, 0, 3},
{3, 3, 3},
{7, 2, 3},
},
{
{1, 3, 4},
{0, 0, 4},
{2, 1, 4},
{3, 2, 4},
},
{
{5, 2, 5},
{4, 1, 5},
{0, 0, 5},
{1, 3, 5},
},
};

28
models/cube.obj Normal file
View File

@ -0,0 +1,28 @@
# Blender 4.1.1
# www.blender.org
o Cube
v 1.000000 1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 1.000000 -1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 1.000000
vn -0.0000 1.0000 -0.0000
vn -0.0000 -0.0000 1.0000
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -1.0000 -0.0000
vn 1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 -1.0000
vt 1.000000 1.000000
vt 0.000000 1.000000
vt 0.000000 0.000000
vt 1.000000 0.000000
s 0
f 1/1/1 5/2/1 7/3/1 3/4/1
f 4/4/2 3/1/2 7/2/2 8/3/2
f 8/3/3 7/2/3 5/1/3 6/4/3
f 6/2/4 2/1/4 4/4/4 8/3/4
f 2/4/5 1/1/5 3/2/5 4/3/5
f 6/3/6 5/2/6 1/1/6 2/4/6

1
models/generate.py Symbolic link
View File

@ -0,0 +1 @@
../registers/generate.py

33
models/model.h Normal file
View File

@ -0,0 +1,33 @@
#pragma once
#include <stdint.h>
struct index_ptn {
uint16_t position;
uint16_t texture;
uint16_t normal;
};
struct face {
struct index_ptn a;
struct index_ptn b;
struct index_ptn c;
struct index_ptn d;
};
struct vertex_position { // signed 4.6 fixed point
uint16_t x;
uint16_t y;
uint16_t z;
};
struct vertex_normal { // s.9 fixed point
uint16_t x;
uint16_t y;
uint16_t z;
};
struct vertex_texture { // s.15 fixed point
uint16_t u;
uint16_t v;
};

View File

@ -0,0 +1,112 @@
from collections import defaultdict
from dataclasses import dataclass
import string
@dataclass
class FixedPoint:
negative: bool
integer: int
fraction: int
point: int
@dataclass
class VertexPosition:
x: FixedPoint
y: FixedPoint
z: FixedPoint
@dataclass
class VertexNormal:
x: FixedPoint
y: FixedPoint
z: FixedPoint
@dataclass
class VertexTexture:
u: FixedPoint
v: FixedPoint
@dataclass
class IndexVTN:
vertex_position: int
vertex_texture: int
vertex_normal: int
@dataclass
class Face:
a: IndexVTN
b: IndexVTN
c: IndexVTN
d: IndexVTN
def parse_fixed_point(s):
negative = s.startswith('-')
s = s.removeprefix('-')
integer, fraction = s.split('.')
assert all(c in string.digits for c in integer), integer
assert all(c in string.digits for c in fraction), fraction
assert len(integer) > 0 and len(fraction) > 0, s
return FixedPoint(
negative,
int(integer),
int(fraction),
10 ** len(fraction)
)
def parse_fixed_point_vector(args, n):
split = args.split()
assert len(split) == n, (n, split)
return tuple(map(parse_fixed_point, split))
def parse_vertex_position(args):
coordinates = parse_fixed_point_vector(args, 3)
yield VertexPosition(*coordinates)
def parse_vertex_normal(args):
coordinates = parse_fixed_point_vector(args, 3)
yield VertexNormal(*coordinates)
def parse_vertex_texture(args):
coordinates = parse_fixed_point_vector(args, 2)
yield VertexTexture(*coordinates)
def int_minus_one(s):
n = int(s) - 1
assert n >= 0
return n
def parse_vertex_indices(args):
indices = args.split('/')
assert len(indices) == 3, indices
return IndexVTN(*map(int_minus_one, indices))
def parse_face(args):
vertices = args.split()
assert len(vertices) == 4, vertices
yield Face(*map(parse_vertex_indices, vertices))
def parse_obj_line(line):
prefixes = [
('v ', parse_vertex_position),
('vn ', parse_vertex_normal),
('vt ', parse_vertex_texture),
('f ', parse_face)
]
for prefix, parser in prefixes:
if line.startswith(prefix):
args = line.removeprefix(prefix)
yield from parser(args)
def group_by_type(l):
grouped = defaultdict(list)
for i in l:
grouped[type(i)].append(i)
return dict(grouped)
def parse_obj_file(buf):
return group_by_type((
parsed
for line in buf.strip().split('\n')
for parsed in parse_obj_line(line)
if not line.startswith('#')
))

View File

@ -0,0 +1,91 @@
from dataclasses import astuple
import sys
from generate import renderer
from parse_obj_fixed_point import parse_obj_file
from parse_obj_fixed_point import VertexPosition
from parse_obj_fixed_point import VertexNormal
from parse_obj_fixed_point import VertexTexture
from parse_obj_fixed_point import Face
vertex_position_fraction_bits = 6 # 4.6
vertex_normal_fraction_bits = 9 # s.9
vertex_texture_fraction_bits = 15 # s.15
def convert_fixed_point(fp, fraction_bits):
point = 2 ** fraction_bits
sign = -1 if fp.negative else 1
n0 = sign * (fp.integer * fp.point + fp.fraction)
n1 = n0 * point // fp.point
return n1
def render_index_vtn(index_vtn):
s = ", ".join(map(str, index_vtn))
yield f"{{{s}}},"
def render_face(face):
yield "{"
for index_vtn in astuple(face):
yield from render_index_vtn(index_vtn)
yield "},"
def render_faces(prefix, faces):
yield f"struct face {prefix}_faces[] = {{"
for face in faces:
yield from render_face(face)
yield "};"
def xyz(vec):
return (vec.x, vec.y, vec.z)
def uv(vec):
return (vec.u, vec.v)
def render_vertex(vertex_tuple, fraction_bits):
s = ", ".join(
str(convert_fixed_point(fp, fraction_bits))
for fp in vertex_tuple
)
yield f"{{{s}}},"
def render_vertices(prefix, name, vertices, fraction_bits):
yield f"// .{fraction_bits} fixed-point"
yield f"struct vertex_{name} {prefix}_{name}s[] = {{"
for vertex in vertices:
yield from render_vertex(vertex, fraction_bits)
yield "};"
def render_vertex_positions(prefix, vertex_positions):
yield from render_vertices(prefix,
"position",
map(xyz, vertex_positions),
vertex_position_fraction_bits)
def render_vertex_normals(prefix, vertex_normals):
yield from render_vertices(prefix,
"normal",
map(xyz, vertex_normals),
vertex_normal_fraction_bits)
def render_vertex_texture(prefix, vertex_textures):
yield from render_vertices(prefix,
"texture",
map(uv, vertex_textures),
vertex_texture_fraction_bits)
def render_header():
yield '#include "model.h"'
yield ""
with open(sys.argv[1]) as f:
buf = f.read()
prefix = sys.argv[2]
d = parse_obj_file(buf)
render, out = renderer()
render(render_header())
render(render_vertex_positions(prefix, d[VertexPosition]))
render(render_vertex_texture(prefix, d[VertexTexture]))
render(render_vertex_normals(prefix, d[VertexNormal]))
render(render_faces(prefix, d[Face]))
sys.stdout.write(out.getvalue())