Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enable encoding and decoding texture coordinates #281

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ BETTERCAMERA ?= 0
NODRAWINGDISTANCE ?= 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

Expand Down Expand Up @@ -502,6 +504,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,358 changes: 10,358 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.
37 changes: 30 additions & 7 deletions c2obj.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
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
Expand All @@ -9,8 +10,8 @@
$ 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.

"""
Expand Down Expand Up @@ -58,8 +59,8 @@ def parse(filename, output_directory):

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

Expand Down Expand Up @@ -107,31 +108,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
133 changes: 97 additions & 36 deletions obj2c.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""
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
Expand All @@ -10,84 +10,137 @@
$ 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 +152,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