Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
7bc25a1
cleaned up level importer, added type hints, tested for bugs a little…
Nov 20, 2022
9a2511b
removed accidentally created file
Nov 20, 2022
35fe6f5
Merge remote-tracking branch 'KCS/fast64_main' into fast64_main
Nov 23, 2022
6fb0c4c
Merge branch 'fast64_main' into importers
Nov 23, 2022
8a47ee5
Merge remote-tracking branch 'fast64/main' into importers
Dec 4, 2022
10d30a1
Merge remote-tracking branch 'fast64/main' into importers
May 10, 2023
5bfec0d
rewrote import plugin to use generic data parser scheme for all parsi…
Jun 29, 2023
3d9a53b
made code cleaner and added type hints
Jun 30, 2023
1a53408
made armature exporting matching for goomba as test, with working ani…
Jul 7, 2023
a7ce46f
fixed file cleaning errors, fixed vtx buffer name parsing issue, fixe…
Sep 10, 2023
17d7b7e
added enums for selecting actor models to import
Sep 10, 2023
d2bc689
Merge remote-tracking branch 'fast64/main' into importers
Feb 17, 2024
33de751
Merge remote-tracking branch 'fast64/main' into importers
Mar 1, 2024
2e47830
merged and fixed a bunch of stuffs and all that you are welcome reonu
Aug 3, 2024
3ef1690
fixed more bugs with importing and added macro parsing for all the de…
Aug 4, 2024
7fba392
merged main
Jan 1, 2025
933676e
fixed plugin UVs on newer blender versions
Jan 1, 2025
c7ceaa1
fixed a bunch of importing bugs, made armatures work, cleaned up code…
Mar 13, 2025
c456be7
added support for linking objects for special objects, added passes f…
Mar 18, 2025
4da4809
profiling, sped up import by approx half, still much more to go
Mar 22, 2025
9833c2b
fixed some gfx, made armature/obj importing dynamic based on geo cont…
Jan 26, 2026
6c680d5
fixed importing for translate rotate nodes, made empty models not imp…
Jan 28, 2026
31d0268
added inverse gamma correction to color registers, fixed light DL cmd…
Jan 30, 2026
c841229
fixed DLs having wrong transform sometimes, fixed lights importing wh…
Jan 31, 2026
5701f97
partially added texture importing from data arrays
Feb 3, 2026
6416611
added macro parsing for nested parenthesis and dealt with usage of CA…
Feb 4, 2026
9365d14
prep for making binary importer, the road will be long
Feb 5, 2026
5bbcf38
updating from laptop:
Feb 5, 2026
05af036
added binary importing for objects and collision, partially done f3d,…
Feb 10, 2026
e7eb35d
f3d unpackoing info5~
Feb 11, 2026
82217aa
binary level importing basics done, cleaned up custom actor importing…
Feb 14, 2026
0fb9da0
fixed lights, geo modes and color combiners for binary importing, unf…
Feb 19, 2026
d9c1a23
added othermode processing
Feb 19, 2026
a37794e
cleaned up code, removed sm64 binary f3d importer
Feb 22, 2026
9abec73
removed old sm64 binary importers for f3d and geo layouts, fixed geo …
Feb 22, 2026
6e9746c
fixed actor presets not working due to duplicate keys in function map…
Feb 23, 2026
522bd30
organized file better, added proper docstrings to most classes and ba…
Feb 23, 2026
b7d15a4
removed test file
Feb 23, 2026
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
198 changes: 198 additions & 0 deletions fast64_internal/bin_png.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# TODO
# current temporary binary/data array image IO tool
# remove print logging

import struct
import math
import zlib
import re
from functools import lru_cache

# ------------------------------------------------------------------------
# helper funcs
# ------------------------------------------------------------------------


# convert data to 8 bit int
@lru_cache(maxsize=255)
def CB_int(val, bits):
return int(((val * 255) + (2 ** (bits - 1)) - 1) / (2 ** (bits) - 1))


def CB_float(val, bits):
return val / (2**bits - 1)


# one bit alpha
def OBA(val):
if val:
return 255
else:
return 0


# unpacks individual bits
# format is csv, num_bits in each param, max is 8
def unpack_bits(format_str: str, byte_stream: bytes, offset: int, length: int):
data_out = []
byte_data = int.from_bytes(byte_stream[offset : offset + length], "big")
byte_mask = (1 << (length * 8)) - 1
# convert length to number of bits in byte data
length = length * 8
for unpack_str in format_str.split(","):
unpack_str = int(unpack_str.strip())
dat_mask = ((1 << unpack_str) - 1) << (8 - unpack_str + length - 8)
data_out.append((byte_data & dat_mask) >> (8 - unpack_str + length - 8))
length = length - unpack_str
return data_out


# written for RGBA16 palette
def get_palette(byte_stream: bytes, im_siz: int):
shifts = [5, 5, 5, 1]
pal_out = []
for tx in range(2**im_siz):
a = unpack_bits("5, 5, 5, 1", byte_stream, tx * 2, 2)
pal_out.append((*(CB_float(c, s) if s > 1 else OBA(c) / 255 for c, s in zip(a, shifts)),))
return pal_out


# ------------------------------------------------------------------------
# image funcs
# ------------------------------------------------------------------------


# byte_stream is bin, image is png
# Alpha changed to true because N64 graphics does not like PNGS with no alpha YES!!!
# Intensity textures must be converted to rgba for blender
def intensity_to_rgba(row_data: list[int]):
out_rows = []
for index, tx in enumerate(row_data):
# alpha texel
if index % 2:
out_rows.append(tx)
else:
out_rows.extend([tx] * 3)
return out_rows


def convert_I_tex(width: int, height: int, im_siz: int, byte_stream: bytes, pal_stream: bytes = None):
if im_siz == 8:
rows = convert_byte_stream(byte_stream, width, height, [8], 1, 1, add_alpha=True)
else:
rows = convert_byte_stream(byte_stream, width, height, [4, 4], 2, 1, add_alpha=True)
return intensity_to_rgba(rows)


def convert_IA_tex(width: int, height: int, im_siz: int, byte_stream: bytes, pal_stream: bytes = None):
if im_siz == 16:
rows = convert_byte_stream(byte_stream, width, height, [8, 8], 1, 2)
if im_siz == 8:
rows = convert_byte_stream(byte_stream, width, height, [4, 4], 1, 1)
else:
rows = convert_byte_stream(byte_stream, width, height, [3, 1, 3, 1], 2, 1)
return intensity_to_rgba(rows)


def convert_RGBA_tex(width: int, height: int, im_siz: int, byte_stream: bytes, pal_stream: bytes = None):
if im_siz == 16:
rows = convert_byte_stream(byte_stream, width, height, [5, 5, 5, 1], 1, 2)
else:
rows = convert_byte_stream(byte_stream, width, height, [8, 8, 8, 8], 1, 4)
return rows


def convert_CI_tex(width: int, height: int, im_siz: int, byte_stream: bytes, pal_stream: bytes = None):
p = get_palette(pal_stream, im_siz)
if im_siz == 4:
rows = convert_byte_stream(byte_stream, width, height, [4, 4], 2, 1, is_ci=True)
else:
rows = convert_byte_stream(byte_stream, width, height, [8], 1, 1, is_ci=True)
out_rows = []
for tx in rows:
out_rows.extend(p[tx])
return out_rows


# change IA31 to IA88 or rgba5551 to rgba8888 as needed
def convert_byte_stream(
byte_stream: bytes,
width: int,
height: int,
channel_bits: list,
tx_per_byte: int,
byte_per_tx: int,
add_alpha: bool = False,
is_ci: bool = False,
):
data_out = []
channel_bits_str = ",".join([str(a) for a in channel_bits])
for im_row in range(height):
# you can only iterate min one byte at a time
# so half width if 4 bit texture
for tx in range(width // tx_per_byte):
tx = tx + (im_row * width // tx_per_byte)
channels = unpack_bits(channel_bits_str, byte_stream, byte_per_tx * tx, byte_per_tx)
if is_ci:
data_out.extend(channels)
continue
for c, s in zip(channels, channel_bits):
if s == 1:
data_out.append(OBA(c) / 255)
elif s < 8:
data_out.append(CB_float(c, s))
elif s == 8:
data_out.append(c / 255)
if add_alpha:
data_out.append(1.0)
return data_out


def convert_tex_bin(fmt: str, width: int, height: int, im_siz: int, byte_stream: bytes, pal_stream: bytes = None):
# I dislike this formulation but calling globals() is worse
funcs = {
"G_IM_FMT_CI": convert_CI_tex,
"G_IM_FMT_I": convert_I_tex,
"G_IM_FMT_IA": convert_IA_tex,
"G_IM_FMT_RGBA": convert_RGBA_tex,
}
im_siz = int(re.search("\d+", im_siz).group())
return funcs.get(fmt)(int(width), int(height), im_siz, byte_stream, pal_stream)


def convert_tex_c(fmt: str, width: int, height: int, im_siz: int, byte_stream: bytes, pal_stream: bytes = None):
# I dislike this formulation but calling globals() is worse
funcs = {
"G_IM_FMT_CI": convert_CI_tex,
"G_IM_FMT_I": convert_I_tex,
"G_IM_FMT_IA": convert_IA_tex,
"G_IM_FMT_RGBA": convert_RGBA_tex,
}
byte_stream = bytes([int(a.strip(), 0x10) for a in byte_stream.split(",") if "0x" in a])
if pal_stream:
pal_stream = bytes([int(a.strip(), 0x10) for a in pal_stream.split(",") if "0x" in a])
im_siz = int(re.search("\d+", im_siz).group())
return funcs.get(fmt)(int(width), int(height), im_siz, byte_stream, pal_stream)


if __name__ == "__main__":
textures = dict()
# with open("tmp_tex_examples.c", "r", newline="") as c_file:
# # For textures, try u8, and s16 aswell
# textures.update(
# get_data_types_from_file(
# c_file,
# {
# "Texture": [None, None],
# "u8": [None, None],
# "s16": [None, None],
# },
# )
# )
# print(list(textures.keys()))
# pal = textures["jrb_dl_E050_O021_CI_ci4_pal_rgba16"]
# tex = textures["jrb_dl_E050_O021_CI_ci4"]
# pal_stream = bytes([int(a.strip(), 0x10) for a in pal.var_data[0].split(",") if "0x" in a])
# tex_stream = bytes([int(a.strip(), 0x10) for a in tex.var_data[0].split(",") if "0x" in a])
# im = convert_tex("CI", 4, 4, 4, pal_stream, tex_stream)
# print(im)
2 changes: 1 addition & 1 deletion fast64_internal/f3d/f3d_gbi.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ def __init__(self, F3D_VER):
self.G_SPRITE2D_BASE = 9 # sprite command

# IMMEDIATE commands
self.G_IMMFIRST = -65
self.G_IMMFIRST = 0xBF
self.G_TRI1 = self.G_IMMFIRST - 0
self.G_CULLDL = self.G_IMMFIRST - 1
self.G_POPMTX = self.G_IMMFIRST - 2
Expand Down
Loading