Skip to content

Commit

Permalink
MERGE PR 281
Browse files Browse the repository at this point in the history
  • Loading branch information
quentin452 committed May 2, 2024
1 parent a661ca2 commit 227585c
Show file tree
Hide file tree
Showing 11 changed files with 27,192 additions and 62 deletions.
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ TARGET_BITS ?= 0

# Disable texture fixes by default (helps with them purists)
TEXTURE_FIX ?= 0
# Disable high poly models by default (all hail the purists)
HD_MODELS ?= 0
# Enable extended options menu by default
EXT_OPTIONS_MENU ?= 1
# Disable text-based save-files by default
Expand Down Expand Up @@ -601,6 +603,12 @@ ifeq ($(TEXTURE_FIX),1)
CFLAGS += -DTEXTURE_FIX
endif

# Check high poly models options
ifeq ($(HD_MODELS),1)
CC_CHECK += -DHD_MODELS
CFLAGS += -DHD_MODELS
endif

# Check for extended options menu option
ifeq ($(EXT_OPTIONS_MENU),1)
CC_CHECK += -DEXT_OPTIONS_MENU
Expand Down
10,300 changes: 10,300 additions & 0 deletions actors/bowser/hd_model.inc.c

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions actors/common1.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ UNUSED static const u64 binid_10 = 10;
#include "warp_collision/collision.inc.c"
UNUSED static const u64 binid_11 = 11;

#ifdef HD_MODELS
#include "mario_cap/hd_model.inc.c"
#else
#include "mario_cap/model.inc.c"
#endif
UNUSED static const u64 binid_12 = 12;

#include "power_meter/model.inc.c"
Expand Down
4 changes: 4 additions & 0 deletions actors/group0.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
#include "make_const_nonconst.h"

// Note: This bin does not use bin IDs, unlike the other segmented bins.
#ifdef HD_MODELS
#include "mario/hd_model.inc.c"
#else
#include "mario/model.inc.c"
#endif

#include "bubble/model.inc.c"

Expand Down
4 changes: 4 additions & 0 deletions actors/group12.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ UNUSED static const u64 binid_1 = 1;
#include "yellow_sphere/model.inc.c"
UNUSED static const u64 binid_2 = 2;

#ifdef HD_MODELS
#include "bowser/hd_model.inc.c"
#else
#include "bowser/model.inc.c"
#endif
#include "bowser/anims/data.inc.c"
#include "bowser/anims/table.inc.c"
#include "bowser/flames_pos.inc.c"
Expand Down
15,862 changes: 15,862 additions & 0 deletions actors/mario/hd_model.inc.c

Large diffs are not rendered by default.

Binary file added actors/mario/mario_logo_custom.rgba16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
875 changes: 875 additions & 0 deletions actors/mario_cap/hd_model.inc.c

Large diffs are not rendered by default.

Binary file added actors/mario_cap/mario_logo_custom.rgba16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
61 changes: 40 additions & 21 deletions c2obj.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
"""
This module attempts to parse the ``model.inc.c`` files and extract the
3D models within as standard Wavefront OBJ files.
3D models (including normals and texture coordinates) within as standard
Wavefront OBJ files.
Example:
Specify the path to the ``.inc.c`` file and a directory where to save
the extracted ``.obj`` files.
$ python c2obj.py ./actors/mario/model.inc.c ./actors/mario/obj/
This is a work in progress and it currently has some serious limitations:
* It only extracts geometry information, so no textures or any other info
* It makes assumptions about the layout of the code in the C source
* It assumes that the encoded texture coordinates are for a 32x32 map
* It hasn't been properly tested.
"""

def parse(filename, output_directory):
Expand All @@ -23,7 +20,7 @@ def parse(filename, output_directory):
mkdir(output_directory)
except OSError:
print(f'Could not use output directory {output_directory}.')

vtx_def = 'static const Vtx '
vtx_data = {}
reading_vtx = False
Expand Down Expand Up @@ -55,14 +52,14 @@ def parse(filename, output_directory):

if line.startswith(gfx_def):
from datetime import datetime

current_gfx_name = line.split(' ')[2][:-2]
current_gfx_file = open(path.join(output_directory, current_gfx_name + '.obj'), 'w')
current_gfx_file.write("# Armando Arredondo's SM64 Wavefront OBJ Geometry Converter\n")
current_gfx_file.write('# File Created: {}\n\n'.format(datetime.now()))
current_gfx_file.write(f"# Armando Arredondo's SM64 Wavefront OBJ Geometry Converter\n")
current_gfx_file.write(f'# File Created: {datetime.now()}\n\n')
reading_gfx = True
continue

if line == end_of_block:
if reading_vtx:
vtx_data[current_vtx_name] = current_vtx_data
Expand All @@ -74,23 +71,23 @@ def parse(filename, output_directory):
current_gfx_vertices = 0
reading_gfx = False
gfx_count += 1

continue

if reading_vtx:
line = line.replace('{', '[').replace('}', ']')
tri = eval(line[:-1])[0]
current_vtx_data.append(tri)
continue

if reading_gfx:
if line.startswith(insert_vert_call):
args = line[len(insert_vert_call):].split(',')
current_vtx_name = args[0]

if current_gfx_vertices > 0:
current_gfx_file.write(f'# {current_gfx_faces} faces\n\n')

current_gfx_faces = 0
current_vtx_vertices = len(vtx_data[current_vtx_name])
current_gfx_vertices += current_vtx_vertices
Expand All @@ -107,31 +104,53 @@ def parse(filename, output_directory):
current_gfx_file.write('vn {:.3f} {:.3f} {:.3f}\n'.format(*n))
current_gfx_file.write(f'# {current_vtx_vertices} vertex normals\n\n')

texture_info = any(_has_texture_info(tri) for tri in current_vtx_data)
if texture_info:
for tri in current_vtx_data:
u = _decode_texture(tri[2][0])
v = 1 - _decode_texture(tri[2][1])
current_gfx_file.write('vt {:.3f} {:.3f}\n'.format(u, v))
current_gfx_file.write(f'# {current_vtx_vertices} texture coords\n\n')

current_gfx_file.write(f'g {current_vtx_name}\n\n')

elif line.startswith(insert_2tri_call):
args = line[len(insert_2tri_call):].split(',')
correction = current_gfx_vertices - current_vtx_vertices + 1
indexes = [eval(args[i]) + correction for i in [0, 1, 2, 4, 5, 6]]
current_gfx_file.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes[:3]))
current_gfx_file.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes[3:]))
if texture_info:
current_gfx_file.write('f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n'.format(*indexes[:3]))
current_gfx_file.write('f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n'.format(*indexes[3:]))
else:
current_gfx_file.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes[:3]))
current_gfx_file.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes[3:]))
current_gfx_faces += 2

elif line.startswith(insert_1tri_call):
args = line[len(insert_1tri_call):].split(',')
correction = current_gfx_vertices - current_vtx_vertices + 1
indexes = [eval(args[i]) + correction for i in [0, 1, 2]]
current_gfx_file.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes))
if texture_info:
current_gfx_file.write('f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n'.format(*indexes))
else:
current_gfx_file.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes))
current_gfx_faces += 1

continue

print(f'{gfx_count} models extracted.')

def _has_texture_info(tri):
(u, v) = tri[2]
return int(u) != 0 or int(v) != 0

def _decode_normal(x):
y = x if x <= 127 else x - 255
return y / 127

def _decode_texture(x):
return x / (2**10)

if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
Expand Down
136 changes: 95 additions & 41 deletions obj2c.py
Original file line number Diff line number Diff line change
@@ -1,93 +1,139 @@
"""
This module generates a fragment of C code, in the style of that found in
the ``model.inc.c`` files, that encodes the geometry of the model specified
by the Wavefront OBJ file.
the ``model.inc.c`` files, that encodes the geometry (vertices, normals and
texture coordinates) of the model specified by the Wavefront OBJ file.
Example:
Specify the path to the ``.obj`` file and pipe the output of the script
into the desired destination ``.c`` file.
$ python obj2c.py left_hand_closed.obj > left_hand_closed.inc.c
This is a work in progress and it currently has some serious limitations:
* It only encodes the geometry information of the OBJ file, so no
texture mapping or any other info.
* The generated fragment of C code has to be manually pasted into the
desired source file. Make sure that the name of the Gfx structure
you're pasting matches the one you're replacing.
* It assumes that the target texture is a 32x32 map
* It hasn't been properly tested.
"""

def parse(filename):
from os.path import basename, splitext
from datetime import datetime
from re import sub

clean = lambda fn: sub('\W|^(?=\d)','_', fn)

# WARNIGN!
# `gfx_name` is just a guess. You have to manually check that the name
# of the Gfx structure you're pasting matches the one you're replacing.
clean = lambda fn: sub('\W|^(?=\d)','_', fn)
gfx_name = clean(splitext(basename(filename))[0])
gfx_name = clean(splitext(basename(filename))[0])
gfx_vertices = []
gfx_normals = []
gfx_texture = []
vertex_to_normal = {}
vertex_to_texture = {}
gfx_v_count = 0

vtx_name = ''
vtx_faces = []
vtx_v_count = 0

output_upper = []
output_upper.append("// Armando Arredondo's SM64 Wavefront OBJ Geometry Converter")
output_upper.append(f'// File Created: {datetime.now()}\n')
output_lower = [f'const Gfx {gfx_name}[] = {{']
reading_vtx = False

def _record_vtx():
nonlocal gfx_vertices
nonlocal gfx_normals
nonlocal gfx_texture
nonlocal vertex_to_normal
nonlocal vertex_to_texture
nonlocal gfx_v_count
nonlocal vtx_name
nonlocal vtx_faces
nonlocal vtx_v_count
nonlocal output_upper
nonlocal output_lower
nonlocal reading_vtx

output_upper.append(f'static const Vtx {vtx_name}[] = {{')
for i in range(gfx_v_count - vtx_v_count, gfx_v_count):
v_string = '[{}, {}, {}]'.format(*gfx_vertices[i])
n_string = '[{}, {}, {}, 0x00]'.format(*gfx_normals[vertex_to_normal[i]])
if i in vertex_to_texture:
t_string = '[{}, {}]'.format(*gfx_texture[vertex_to_texture[i]])
else:
t_string = '[0, 0]'

combined = f' [[{v_string}, 0, {t_string}, {n_string}]],'
output_upper.append(combined.replace('[', '{').replace(']', '}'))

output_upper.append('};\n')
output_lower.append(f' gsSPVertex({vtx_name}, {vtx_v_count}, 0),')

n = len(vtx_faces)
correction = vtx_v_count - gfx_v_count - 1
for i in range(int(n / 2)):
f1 = [vtx_faces[2 * i][j] + correction for j in range(3)]
f2 = [vtx_faces[2 * i + 1][j] + correction for j in range(3)]
output_lower.append(' gsSP2Triangles({}, {}, {}, 0x0, {}, {}, {}, 0x0),'.format(*f1, *f2))

if n % 2 != 0:
f3 = [vtx_faces[-1][j] + correction for j in range(3)]
output_lower.append(' gsSP1Triangle({}, {}, {}, 0x0),'.format(*f3))

vtx_v_count = 0
vtx_faces = []
reading_vtx = False

with open(filename, 'r') as obj:
for line in obj:
line = line.strip()

if line.startswith('v '):
if reading_vtx:
_record_vtx()
coordinates = [eval(x) for x in line.split()[1:4]]
gfx_vertices.append(coordinates)
vtx_v_count += 1
gfx_v_count += 1

continue

if line.startswith('vn '):
if reading_vtx:
_record_vtx()
coordinates = [eval(x) for x in line.split()[1:4]]
gfx_normals.append([_encode_normal(x) for x in coordinates])
continue

if line.startswith('vt '):
if reading_vtx:
_record_vtx()
coordinates = line.split()
u = eval(coordinates[1])
v = 1 - eval(coordinates[2])
gfx_texture.append([_encode_texture(u), _encode_texture(v)])
continue

if line.startswith('g '):
vtx_name = line.split()[1]
reading_vtx = True
continue

if line.startswith('f '):
pairs = [pair.split('//') for pair in line.split()[1:4]]
vtx_faces.append([int(pair[0]) for pair in pairs])
for (x, y) in pairs:
vertex_to_normal[int(x) - 1] = int(y) - 1

if line.startswith('# ') and line.endswith('faces'):
output_upper.append(f'static const Vtx {vtx_name}[] = {{')
for i in range(gfx_v_count - vtx_v_count, gfx_v_count):
v_string = '[{}, {}, {}]'.format(*gfx_vertices[i])
n_string = '[{}, {}, {}, 0x00]'.format(*gfx_normals[vertex_to_normal[i]])
combined = f' [[{v_string}, 0, [0, 0], {n_string}]],'
output_upper.append(combined.replace('[', '{').replace(']', '}'))

output_upper.append('};\n')
output_lower.append(f' gsSPVertex({vtx_name}, {vtx_v_count}, 0),')

n = len(vtx_faces)
correction = vtx_v_count - gfx_v_count - 1
for i in range(int(n / 2)):
f1 = [vtx_faces[2 * i][j] + correction for j in range(3)]
f2 = [vtx_faces[2 * i + 1][j] + correction for j in range(3)]
output_lower.append(' gsSP2Triangles({}, {}, {}, 0x0, {}, {}, {}, 0x0),'.format(*f1, *f2))

if n % 2 != 0:
f3 = [vtx_faces[-1][j] + correction for j in range(3)]
output_lower.append(' gsSP1Triangle({}, {}, {}, 0x0),'.format(*f3))

vtx_v_count = 0
vtx_faces = []

_assert(reading_vtx)
sets = [pair.split('/') for pair in line.split()[1:4]]
vtx_faces.append([int(s[0]) for s in sets])
for (x, y, z) in sets:
vertex_to_normal[int(x) - 1] = int(z) - 1
try:
vertex_to_texture[int(x) - 1] = int(y) - 1
except:
pass
continue

_assert(reading_vtx)
_record_vtx()
output_lower.append(' gsSPEndDisplayList(),')
output_lower.append('};')

Expand All @@ -99,6 +145,14 @@ def _encode_normal(x):
if x <= 0: x += 255
return hex(int(x))

def _encode_texture(x):
from math import floor
return floor(x * 32) << 5

def _assert(p):
if not p:
raise RuntimeError("Unrecognized format in input file")

if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
Expand Down

0 comments on commit 227585c

Please sign in to comment.