From ab0cebab7afb72835d6f1dc10d7a68a56909749b Mon Sep 17 00:00:00 2001 From: Gregory Heskett Date: Mon, 18 Sep 2023 09:10:56 -0400 Subject: [PATCH] Rotate envmaps to be consistent with vanilla (#636) * Start work on envmap rotation stuff * n64graphic envmap rotation working * Add comment to rotation function * Fix DLs of non 32x32 env maps, fix envmap rotation * fix metal flying vanish cap * fix metal mario's medium poly butt * new asset version for flipped env maps * added missing env textures to extract script * restore asset_needs_update * Skip asset_needs_update calls if local version matches new version * removed the goddard textures from envmap rotation --------- Co-authored-by: mineqwerty Co-authored-by: thecozies <79979276+thecozies@users.noreply.github.com> --- actors/mario/model.inc.c | 32 +++++++++--------- actors/mario_cap/model.inc.c | 16 +++------ actors/water_ring/model.inc.c | 6 ++-- extract_assets.py | 42 +++++++++++++++++++++-- include/config/config_graphics.h | 7 ---- src/game/rendering_graph_node.c | 14 +++----- tools/n64graphics.c | 58 +++++++++++++++++++++++++++++++- 7 files changed, 126 insertions(+), 49 deletions(-) diff --git a/actors/mario/model.inc.c b/actors/mario/model.inc.c index 28da06dd..4d8fed76 100644 --- a/actors/mario/model.inc.c +++ b/actors/mario/model.inc.c @@ -368,8 +368,8 @@ const Gfx mario_metal_butt[] = { gsDPPipeSync(), gsSPSetGeometryMode(G_TEXTURE_GEN), gsDPSetCombineMode(G_CC_DECALFADE, G_CC_DECALFADE), - gsDPLoadTextureBlock(mario_texture_metal, G_IM_FMT_RGBA, G_IM_SIZ_16b, 64, 32, 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, 6, 5, G_TX_NOLOD, G_TX_NOLOD), - gsSPTexture(0x0F80, 0x07C0, 0, G_TX_RENDERTILE, G_ON), + gsDPLoadTextureBlock(mario_texture_metal, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 64, 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, 5, 6, G_TX_NOLOD, G_TX_NOLOD), + gsSPTexture(0x07C0, 0x0F80, 0, G_TX_RENDERTILE, G_ON), gsSPLightColor(LIGHT_1, 0xffffffff), gsSPLightColor(LIGHT_2, 0x7f7f7fff), gsSPDisplayList(mario_butt_dl), @@ -966,8 +966,8 @@ const Gfx mario_metal_left_thigh[] = { gsDPPipeSync(), gsSPSetGeometryMode(G_TEXTURE_GEN), gsDPSetCombineMode(G_CC_DECALFADE, G_CC_DECALFADE), - gsDPLoadTextureBlock(mario_texture_metal, G_IM_FMT_RGBA, G_IM_SIZ_16b, 64, 32, 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, 6, 5, G_TX_NOLOD, G_TX_NOLOD), - gsSPTexture(0x0F80, 0x07C0, 0, G_TX_RENDERTILE, G_ON), + gsDPLoadTextureBlock(mario_texture_metal, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 64, 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, 5, 6, G_TX_NOLOD, G_TX_NOLOD), + gsSPTexture(0x07C0, 0x0F80, 0, G_TX_RENDERTILE, G_ON), gsSPLightColor(LIGHT_1, 0xffffffff), gsSPLightColor(LIGHT_2, 0x7f7f7fff), gsSPDisplayList(mario_left_thigh_dl), @@ -3249,8 +3249,8 @@ const Gfx mario_metal_medium_poly_butt[] = { gsDPPipeSync(), gsSPSetGeometryMode(G_TEXTURE_GEN), gsDPSetCombineMode(G_CC_DECALFADE, G_CC_DECALFADE), - gsDPLoadTextureBlock(mario_texture_metal, G_IM_FMT_RGBA, G_IM_SIZ_16b, 64, 32, 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, 6, 5, G_TX_NOLOD, G_TX_NOLOD), - gsSPTexture(0x0F80, 0x07C0, 0, G_TX_RENDERTILE, G_ON), + gsDPLoadTextureBlock(mario_texture_metal, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 64, 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, 5, 6, G_TX_NOLOD, G_TX_NOLOD), + gsSPTexture(0x07C0, 0x0F80, 0, G_TX_RENDERTILE, G_ON), gsSPLightColor(LIGHT_1, 0xffffffff), gsSPLightColor(LIGHT_2, 0x7f7f7fff), gsSPDisplayList(mario_medium_poly_butt_dl), @@ -3638,8 +3638,8 @@ const Gfx mario_metal_medium_poly_left_thigh[] = { gsDPPipeSync(), gsSPSetGeometryMode(G_TEXTURE_GEN), gsDPSetCombineMode(G_CC_DECALFADE, G_CC_DECALFADE), - gsDPLoadTextureBlock(mario_texture_metal, G_IM_FMT_RGBA, G_IM_SIZ_16b, 64, 32, 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, 6, 5, G_TX_NOLOD, G_TX_NOLOD), - gsSPTexture(0x0F80, 0x07C0, 0, G_TX_RENDERTILE, G_ON), + gsDPLoadTextureBlock(mario_texture_metal, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 64, 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, 5, 6, G_TX_NOLOD, G_TX_NOLOD), + gsSPTexture(0x07C0, 0x0F80, 0, G_TX_RENDERTILE, G_ON), gsSPLightColor(LIGHT_1, 0xffffffff), gsSPLightColor(LIGHT_2, 0x7f7f7fff), gsSPDisplayList(mario_medium_poly_left_thigh_dl), @@ -4128,8 +4128,8 @@ const Gfx mario_metal_low_poly_butt[] = { gsDPPipeSync(), gsSPSetGeometryMode(G_TEXTURE_GEN), gsDPSetCombineMode(G_CC_DECALFADE, G_CC_DECALFADE), - gsDPLoadTextureBlock(mario_texture_metal, G_IM_FMT_RGBA, G_IM_SIZ_16b, 64, 32, 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, 6, 5, G_TX_NOLOD, G_TX_NOLOD), - gsSPTexture(0x0F80, 0x07C0, 0, G_TX_RENDERTILE, G_ON), + gsDPLoadTextureBlock(mario_texture_metal, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 64, 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, 5, 6, G_TX_NOLOD, G_TX_NOLOD), + gsSPTexture(0x07C0, 0x0F80, 0, G_TX_RENDERTILE, G_ON), gsSPLightColor(LIGHT_1, 0xffffffff), gsSPLightColor(LIGHT_2, 0x7f7f7fff), gsSPDisplayList(mario_low_poly_butt_dl), @@ -4373,8 +4373,8 @@ const Gfx mario_metal_low_poly_left_thigh[] = { gsDPPipeSync(), gsSPSetGeometryMode(G_TEXTURE_GEN), gsDPSetCombineMode(G_CC_DECALFADE, G_CC_DECALFADE), - gsDPLoadTextureBlock(mario_texture_metal, G_IM_FMT_RGBA, G_IM_SIZ_16b, 64, 32, 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, 6, 5, G_TX_NOLOD, G_TX_NOLOD), - gsSPTexture(0x0F80, 0x07C0, 0, G_TX_RENDERTILE, G_ON), + gsDPLoadTextureBlock(mario_texture_metal, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 64, 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, 5, 6, G_TX_NOLOD, G_TX_NOLOD), + gsSPTexture(0x07C0, 0x0F80, 0, G_TX_RENDERTILE, G_ON), gsSPLightColor(LIGHT_1, 0xffffffff), gsSPLightColor(LIGHT_2, 0x7f7f7fff), gsSPDisplayList(mario_low_poly_left_thigh_dl), @@ -6608,8 +6608,8 @@ const Gfx mario_metal_cap_unused_dl[] = { gsDPPipeSync(), gsSPSetGeometryMode(G_TEXTURE_GEN), gsDPSetCombineMode(G_CC_MODULATERGB, G_CC_MODULATERGB), - gsDPLoadTextureBlock(mario_texture_metal, G_IM_FMT_RGBA, G_IM_SIZ_16b, 64, 32, 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, 6, 5, G_TX_NOLOD, G_TX_NOLOD), - gsSPTexture(0x0F80, 0x07C0, 0, G_TX_RENDERTILE, G_ON), + gsDPLoadTextureBlock(mario_texture_metal, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 64, 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, 5, 6, G_TX_NOLOD, G_TX_NOLOD), + gsSPTexture(0x07C0, 0x0F80, 0, G_TX_RENDERTILE, G_ON), gsSPLightColor(LIGHT_1, 0xffffffff), gsSPLightColor(LIGHT_2, 0x7f7f7fff), gsSPDisplayList(mario_cap_unused_m_logo_dl), @@ -6787,7 +6787,7 @@ const Gfx mario_metal_cap_wings_transparent[] = { gsDPPipeSync(), gsSPSetGeometryMode(G_TEXTURE_GEN | G_CULL_BACK | G_SHADING_SMOOTH), gsDPSetCombineMode(G_CC_DECALFADE, G_CC_DECALFADE), - gsDPLoadTextureBlock(mario_texture_metal, G_IM_FMT_RGBA, G_IM_SIZ_16b, 64, 32, 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, 6, 5, G_TX_NOLOD, G_TX_NOLOD), - gsSPTexture(0x0F80, 0x07C0, 0, G_TX_RENDERTILE, G_ON), + gsDPLoadTextureBlock(mario_texture_metal, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 64, 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, 5, 6, G_TX_NOLOD, G_TX_NOLOD), + gsSPTexture(0x07C0, 0x0F80, 0, G_TX_RENDERTILE, G_ON), gsSPEndDisplayList(), }; diff --git a/actors/mario_cap/model.inc.c b/actors/mario_cap/model.inc.c index b6e9b2f9..a26f6348 100644 --- a/actors/mario_cap/model.inc.c +++ b/actors/mario_cap/model.inc.c @@ -271,8 +271,8 @@ const Gfx mario_cap_seg3_dl_03022FF8[] = { gsDPPipeSync(), gsSPSetGeometryMode(G_TEXTURE_GEN), gsDPSetCombineMode(G_CC_MODULATERGBFADE, G_CC_MODULATERGBFADE), - gsDPLoadTextureBlock(mario_cap_seg3_texture_0301CF50, G_IM_FMT_RGBA, G_IM_SIZ_16b, 64, 32, 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, 6, 5, G_TX_NOLOD, G_TX_NOLOD), - gsSPTexture(0x0F80, 0x07C0, 0, G_TX_RENDERTILE, G_ON), + gsDPLoadTextureBlock(mario_cap_seg3_texture_0301CF50, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 64, 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, 5, 6, G_TX_NOLOD, G_TX_NOLOD), + gsSPTexture(0x07C0, 0x0F80, 0, G_TX_RENDERTILE, G_ON), gsSPLightColor(LIGHT_1, 0xffffffff), gsSPLightColor(LIGHT_2, 0x7f7f7fff), gsSPDisplayList(mario_cap_seg3_dl_03022B30), @@ -281,7 +281,7 @@ const Gfx mario_cap_seg3_dl_03022FF8[] = { gsDPPipeSync(), gsSPClearGeometryMode(G_TEXTURE_GEN), gsDPSetCombineMode(G_CC_SHADE, G_CC_SHADE), - gsSPTexture(0x0F80, 0x07C0, 0, G_TX_RENDERTILE, G_OFF), + gsSPTexture(0x07C0, 0x0F80, 0, G_TX_RENDERTILE, G_OFF), gsDPSetAlphaCompare(G_AC_NONE), gsDPSetEnvColor(255, 255, 255, 255), gsSPEndDisplayList(), @@ -365,14 +365,8 @@ const Gfx mario_cap_seg3_dl_03023298[] = { gsDPPipeSync(), gsSPSetGeometryMode(G_TEXTURE_GEN), gsDPSetCombineMode(G_CC_MODULATERGBFADE, G_CC_MODULATERGBFADE), - gsDPSetTextureImage(G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, mario_cap_seg3_texture_0301CF50), - gsDPSetTile(G_IM_FMT_RGBA, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0, G_TX_WRAP | G_TX_NOMIRROR, 5, G_TX_NOLOD, G_TX_WRAP | G_TX_NOMIRROR, 6, G_TX_NOLOD), - gsDPLoadSync(), - gsDPLoadBlock(G_TX_LOADTILE, 0, 0, 64 * 32 - 1, CALC_DXT(64, G_IM_SIZ_16b_BYTES)), - gsDPPipeSync(), - gsDPSetTile(G_IM_FMT_RGBA, G_IM_SIZ_16b, 16, 0, G_TX_RENDERTILE, 0, G_TX_WRAP | G_TX_NOMIRROR, 5, G_TX_NOLOD, G_TX_WRAP | G_TX_NOMIRROR, 6, G_TX_NOLOD), - gsDPSetTileSize(0, 0, 0, (64 - 1) << G_TEXTURE_IMAGE_FRAC, (32 - 1) << G_TEXTURE_IMAGE_FRAC), - gsSPTexture(0x0F80, 0x07C0, 0, G_TX_RENDERTILE, G_ON), + gsDPLoadTextureBlock(mario_cap_seg3_texture_0301CF50, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 64, 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, 5, 6, G_TX_NOLOD, G_TX_NOLOD), + gsSPTexture(0x07C0, 0x0F80, 0, G_TX_RENDERTILE, G_ON), gsSPLightColor(LIGHT_1, 0xffffffff), gsSPLightColor(LIGHT_2, 0x7f7f7fff), gsSPDisplayList(mario_cap_seg3_dl_03022B30), diff --git a/actors/water_ring/model.inc.c b/actors/water_ring/model.inc.c index 2c710132..8c04dcb3 100644 --- a/actors/water_ring/model.inc.c +++ b/actors/water_ring/model.inc.c @@ -160,8 +160,8 @@ const Gfx water_ring_seg6_dl_06013AC0[] = { gsDPPipeSync(), gsSPSetGeometryMode(G_TEXTURE_GEN), gsDPSetCombineMode(G_CC_DECALFADE, G_CC_DECALFADE), - gsDPLoadTextureBlock(water_ring_seg6_texture_06012380, G_IM_FMT_RGBA, G_IM_SIZ_16b, 64, 32, 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, 6, 5, G_TX_NOLOD, G_TX_NOLOD), - gsSPTexture(0x1800, 0x07C0, 0, G_TX_RENDERTILE, G_ON), + gsDPLoadTextureBlock(water_ring_seg6_texture_06012380, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 64, 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, 5, 6, G_TX_NOLOD, G_TX_NOLOD), + gsSPTexture(0x07C0, 0x1800, 0, G_TX_RENDERTILE, G_ON), gsSPLightColor(LIGHT_1, 0xffffffff), gsSPLightColor(LIGHT_2, 0x3f3f3fff), gsSPVertex(water_ring_seg6_vertex_06013380, 16, 0), @@ -212,6 +212,6 @@ const Gfx water_ring_seg6_dl_06013AC0[] = { gsSPClearGeometryMode(G_TEXTURE_GEN), gsDPSetEnvColor(255, 255, 255, 255), gsDPSetCombineMode(G_CC_SHADE, G_CC_SHADE), - gsSPTexture(0x0F80, 0x07C0, 0, G_TX_RENDERTILE, G_OFF), + gsSPTexture(0x07C0, 0x0F80, 0, G_TX_RENDERTILE, G_OFF), gsSPEndDisplayList(), }; diff --git a/extract_assets.py b/extract_assets.py index 45e30fa8..90928bc3 100755 --- a/extract_assets.py +++ b/extract_assets.py @@ -6,6 +6,19 @@ import subprocess from tools.detect_baseroms import get_rom_candidates +envmap_table = set([ + "actors/mario/mario_metal.rgba16.png", + "actors/mario_cap/mario_cap_metal.rgba16.png", + "actors/star/star_surface.rgba16.png", + "actors/water_bubble/water_bubble.rgba16.png", + "actors/water_ring/water_ring.rgba16.png", + "levels/castle_inside/29.rgba16.png", + "levels/castle_inside/30.rgba16.png", + "levels/hmc/7.rgba16.png", + "levels/castle_inside/16.ia16.png", + "levels/cotmc/2.rgba16.png" +]) + def read_asset_map(): with open("assets.json") as f: ret = json.load(f) @@ -20,6 +33,26 @@ def read_local_asset_list(f): ret.append(line.strip()) return ret +def asset_needs_update(asset, version): + if version <= 7 and asset in envmap_table: + return True + if version <= 6 and asset in ["actors/king_bobomb/king_bob-omb_eyes.rgba16.png", "actors/king_bobomb/king_bob-omb_hand.rgba16.png"]: + return True + if version <= 5 and asset == "textures/spooky/bbh_textures.00800.rgba16.png": + return True + if version <= 4 and asset in ["textures/mountain/ttm_textures.01800.rgba16.png", "textures/mountain/ttm_textures.05800.rgba16.png"]: + return True + if version <= 3 and asset == "textures/cave/hmc_textures.01800.rgba16.png": + return True + if version <= 2 and asset == "textures/inside/inside_castle_textures.09000.rgba16.png": + return True + if version <= 1 and asset.endswith(".m64"): + return True + if version <= 0 and asset.endswith(".aiff"): + return True + return False + + def remove_file(fname): os.remove(fname) print("deleting", fname) @@ -44,7 +77,7 @@ def clean_assets(local_asset_file): def main(): # In case we ever need to change formats of generated files, we keep a # revision ID in the local asset file. - new_version = 7 + new_version = 8 try: local_asset_file = open(".assets-local.txt") @@ -117,7 +150,7 @@ def main(): todo = defaultdict(lambda: []) for (asset, data, exists) in all_assets: # Leave existing assets alone if they have a compatible version. - if exists: + if exists and not (local_version == new_version or asset_needs_update(asset, local_version)): continue meta = data[:-2] @@ -226,6 +259,9 @@ def main(): check=True, ) else: + rotate_envmap = "false" + if asset in envmap_table: + rotate_envmap = "true" w, h = meta fmt = asset.split(".")[-2] subprocess.run( @@ -241,6 +277,8 @@ def main(): str(w), "-h", str(h), + "-r", + rotate_envmap, ], check=True, ) diff --git a/include/config/config_graphics.h b/include/config/config_graphics.h index c252e991..03d8ff11 100644 --- a/include/config/config_graphics.h +++ b/include/config/config_graphics.h @@ -116,13 +116,6 @@ */ // #define PUPPYLIGHTS -/** - * Uses the correct "up" vector for the guLookAtReflect call in geo_process_master_list_sub. - * It is sideways in vanilla, and since vanilla's environment map textures are sideways too, those will appear as sideways in-game if this is enabled. - * Make sure your custom environment map textures are the correct orientation. - */ -// #define FIX_REFLECT_MTX - /** * Disables all object shadows. You'll probably only want this either as a last resort for performance or if you're making a super stylized hack. */ diff --git a/src/game/rendering_graph_node.c b/src/game/rendering_graph_node.c index f435d6ec..c5d5c97b 100644 --- a/src/game/rendering_graph_node.c +++ b/src/game/rendering_graph_node.c @@ -696,21 +696,17 @@ void geo_process_camera(struct GraphNodeCamera *node) { // As a result, environment mapping is broken on Fast3DEX2 without the // changes below. Mat4* cameraMatrix = &gCameraTransform; - #ifdef FIX_REFLECT_MTX + /** + * HackerSM64 2.1: Now uses the correct "up" vector for the guLookAtReflect call in geo_process_master_list_sub. + * It was originally sideways in vanilla, with vanilla's environment map textures sideways to accommodate, but those + * textures are now rotated automatically on extraction to allow for this to be fixed. + */ gCurLookAt->l[0].l.dir[0] = (s8)(127.0f * (*cameraMatrix)[0][0]); gCurLookAt->l[0].l.dir[1] = (s8)(127.0f * (*cameraMatrix)[1][0]); gCurLookAt->l[0].l.dir[2] = (s8)(127.0f * (*cameraMatrix)[2][0]); gCurLookAt->l[1].l.dir[0] = (s8)(127.0f * -(*cameraMatrix)[0][1]); gCurLookAt->l[1].l.dir[1] = (s8)(127.0f * -(*cameraMatrix)[1][1]); gCurLookAt->l[1].l.dir[2] = (s8)(127.0f * -(*cameraMatrix)[2][1]); - #else - gCurLookAt->l[0].l.dir[0] = (s8)(127.0f * (*cameraMatrix)[0][0]); - gCurLookAt->l[0].l.dir[1] = (s8)(127.0f * (*cameraMatrix)[1][0]); - gCurLookAt->l[0].l.dir[2] = (s8)(127.0f * (*cameraMatrix)[2][0]); - gCurLookAt->l[1].l.dir[0] = (s8)(127.0f * (*cameraMatrix)[0][1]); - gCurLookAt->l[1].l.dir[1] = (s8)(127.0f * (*cameraMatrix)[1][1]); - gCurLookAt->l[1].l.dir[2] = (s8)(127.0f * (*cameraMatrix)[2][1]); - #endif #endif // F3DEX_GBI_2 // Make a copy of the view matrix and scale its translation based on WORLD_SCALE diff --git a/tools/n64graphics.c b/tools/n64graphics.c index 0e4513e1..30d1060b 100644 --- a/tools/n64graphics.c +++ b/tools/n64graphics.c @@ -38,6 +38,45 @@ typedef struct // N64 RGBA/IA/I/CI -> internal RGBA/IA //--------------------------------------------------------- +// Rotate raw image counterclockwise (width and height need swapped externally) +void rotate_raw_img(uint8_t *raw, int width, int height, int depth) { + uint8_t *tmp_rotated; + int bytes; + int size; + + if (depth == 32) { + bytes = 4; + } else if (depth == 16) { + bytes = 2; + } else { + ERROR("rotated raw image does not have a valid depth"); + return; + } + + size = width * height * bytes; + + tmp_rotated = malloc(size); + if (!tmp_rotated) { + ERROR("Error allocating %d bytes\n", size); + return; + } + + int offset_src = 0; + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + int offset_dst = (((width - j - 1) * height) + i) * bytes; + for (int k = 0; k < bytes; k++) { + tmp_rotated[offset_dst + k] = raw[offset_src++]; + } + } + } + + bcopy(tmp_rotated, raw, size); + + free(tmp_rotated); + tmp_rotated = NULL; +} + rgba *raw2rgba(const uint8_t *raw, int width, int height, int depth) { rgba *img; @@ -602,6 +641,7 @@ typedef struct int height; int bin_truncate; int pal_truncate; + int rotate_envmap; } graphics_config; static const graphics_config default_config = @@ -619,6 +659,7 @@ static const graphics_config default_config = .height = 32, .bin_truncate = 1, .pal_truncate = 1, + .rotate_envmap = 0, }; typedef struct @@ -701,7 +742,7 @@ static int parse_encoding(write_encoding *encoding, const char *str) static void print_usage(void) { - ERROR("Usage: n64graphics -e/-i BIN_FILE -g IMG_FILE [-p PAL_FILE] [-o BIN_OFFSET] [-P PAL_OFFSET] [-f FORMAT] [-c CI_FORMAT] [-w WIDTH] [-h HEIGHT] [-V]\n" + ERROR("Usage: n64graphics -e/-i BIN_FILE -g IMG_FILE [-p PAL_FILE] [-o BIN_OFFSET] [-P PAL_OFFSET] [-f FORMAT] [-c CI_FORMAT] [-w WIDTH] [-h HEIGHT] [-r ROTATE] [-V]\n" "\n" "n64graphics v" N64GRAPHICS_VERSION ": N64 graphics manipulator\n" "\n" @@ -715,6 +756,7 @@ static void print_usage(void) " -s SCHEME output scheme: raw, u8 (hex), u64 (hex) (default: %s)\n" " -w WIDTH export texture width (default: %d)\n" " -h HEIGHT export texture height (default: %d)\n" + " -r ROTATE rotate envmap texture for rgba extraction only (default: false)\n" "CI arguments:\n" " -c CI_FORMAT CI palette format: rgba16, ia16 (default: %s)\n" " -p PAL_FILE palette binary file to import/export from/to\n" @@ -787,6 +829,14 @@ static int parse_arguments(int argc, char *argv[], graphics_config *config) config->pal_offset = strtoul(argv[i], NULL, 0); config->pal_truncate = 0; break; + case 'r': + if (++i >= argc) return 0; + if (!strcmp(argv[i], "true") || !strcmp(argv[i], "True") || strtoul(argv[i], NULL, 0) == 1) { + config->rotate_envmap = 1; + } else { + config->rotate_envmap = 0; + } + break; case 's': if (++i >= argc) return 0; if (!parse_encoding(&config->encoding, argv[i])) { @@ -1006,6 +1056,12 @@ int main(int argc, char *argv[]) } switch (config.format.format) { case IMG_FORMAT_RGBA: + if (config.rotate_envmap) { + rotate_raw_img(raw, config.width, config.height, config.format.depth); + int tmp_height = config.height; + config.height = config.width; + config.width = tmp_height; + } imgr = raw2rgba(raw, config.width, config.height, config.format.depth); res = rgba2png(config.img_filename, imgr, config.width, config.height); break;