Members MilesMontana Posted January 18 Members Posted January 18 Perhaps. I just find it odd that everywhere else for the most part seems to do fine, but this is the one that seems to raise the most hell over material assignments.
Members Über Winfrey Posted January 19 Members Posted January 19 10 hours ago, MilesMontana said: Hmm, noted. Kinda odd a bunch of those meshes went for one material when there's meant to be multiple, but I assume it's something to do with the way the file is set up versus the others? I never usually saw too many problems with the other maps, but that's the one that seems to fight the most. the textures are importing, it just looks like a bug with the material builder, which I'll ahve to figure out
Members MilesMontana Posted January 19 Members Posted January 19 I guess you've also seen the weirdness with BeachRunup as well then? Now that's some funky looking stuff.
Members Über Winfrey Posted January 19 Members Posted January 19 2 minutes ago, MilesMontana said: I guess you've also seen the weirdness with BeachRunup as well then? Now that's some funky looking stuff. I haven't, only time I looked at them was when the script only imported the diffuse texture
Members MilesMontana Posted January 19 Members Posted January 19 2 hours ago, Über Winfrey said: I haven't, only time I looked at them was when the script only imported the diffuse texture Yeah, not exactly a pretty sight. Oddly more missing than expected.
Members Über Winfrey Posted January 19 Members Posted January 19 (edited) 8 hours ago, MilesMontana said: Yeah, not exactly a pretty sight. Oddly more missing than expected. hmmm, well shit, definitely didn't look like that last time I loaded it in, why these maps be so complicate, I'll look into it so here's the issue, there are blocks of vertexbuffers that are not receiving faces because a previous vertexblock is stealing them, Which I thought I had fixed, I'll have to dig in and see whats going on === pushbuffers contents === [fallback-pb] assigned 0 fallback static pushbuffers === FACE BUILD === 'ndVertexBuffer2_00014328' → 32 faces, 16 blocks 'ndVertexBuffer2_00014C68' → 4096 faces, 208 blocks 'ndVertexBuffer2_00015ED8' → 1280 faces, 16 blocks 'ndVertexBuffer2_00016AFC' → 384 faces, 48 blocks 'ndVertexBuffer2_00017798' → 384 faces, 48 blocks 'ndVertexBuffer2_00018418' → 1536 faces, 128 blocks 'ndVertexBuffer2_000193D8' → 205792 faces, 1408 blocks 'ndVertexBuffer2_00022934' → 55296 faces, 176 blocks 'ndVertexBuffer2_00026888' → 80 faces, 48 blocks 'ndVertexBuffer2_000276D8' → 0 faces, 0 blocks 'ndVertexBuffer2_000281E8' → 64 faces, 32 blocks 'ndVertexBuffer2_00028EE8' → 1024 faces, 144 blocks 'ndVertexBuffer2_00029F28' → 640 faces, 112 blocks 'ndVertexBuffer2_0002ABA8' → 1088 faces, 64 blocks 'ndVertexBuffer2_0002BE48' → 592 faces, 96 blocks 'ndVertexBuffer2_0002CE88' → 9008 faces, 528 blocks 'ndVertexBuffer2_00030418' → 4624 faces, 288 blocks 'ndVertexBuffer2_00031778' → 736 faces, 112 blocks 'ndVertexBuffer2_00032538' → 784 faces, 64 blocks 'ndVertexBuffer2_00033258' → 8736 faces, 176 blocks 'ndVertexBuffer2_00034B28' → 768 faces, 128 blocks 'ndVertexBuffer2_00035EC8' → 928 faces, 64 blocks 'ndVertexBuffer2_00036D28' → 1664 faces, 144 blocks 'ndVertexBuffer2_00038D28' → 0 faces, 0 blocks 'ndVertexBuffer2_0003A548' → 17568 faces, 144 blocks 'ndVertexBuffer2_0003C1C8' → 896 faces, 48 blocks 'ndVertexBuffer2_0003D028' → 0 faces, 0 blocks 'ndVertexBuffer2_0003DFB8' → 0 faces, 0 blocks 'ndVertexBuffer2_0003F068' → 32 faces, 32 blocks 'ndVertexBuffer2_000433C8' → 0 faces, 0 blocks 'ndVertexBuffer2_00043D98' → 1344 faces, 32 blocks 'ndVertexBuffer2_00044958' → 1792 faces, 32 blocks 'ndVertexBuffer2_00045564' → 33 faces, 2 blocks 'ndVertexBuffer2_00047188' → 224 faces, 32 blocks 'ndVertexBuffer2_00048028' → 0 faces, 0 blocks 'ndVertexBuffer2_00048EAC' → 0 faces, 0 blocks 'ndVertexBuffer2_00049748' → 1152 faces, 48 blocks Edited January 19 by Über Winfrey
Members MilesMontana Posted January 19 Members Posted January 19 Two steps forward, one step back ordeal. Strange that they were there before and as soon as you started figuring more out, it decides to then give more hell.
Members Über Winfrey Posted January 20 Members Posted January 20 (edited) I figured it out, Faces were a different format that I didn't add to implementation, which I'm working on now, I would have had this done sooner but I never thought about checking face table format, whoops Edited January 20 by Über Winfrey 2
Members MilesMontana Posted January 20 Members Posted January 20 Happens. At least you figured out what went wrong and got it the way you wanted it.
Members Über Winfrey Posted January 20 Members Posted January 20 (edited) once I figure out what is going on with the material problem with the pub, I'll release the updated script but if you want to update the script yourself to get the map mesh to load, here are the 2 functions that need to be replaced def build_faces(self): import struct from collections import defaultdict print("=== FACE BUILD ===") total = 0 if not hasattr(self, "_block_face_ranges"): self._block_face_ranges = defaultdict(list) else: self._block_face_ranges.clear() def _staticpush_ptr_from_table(table): if not table: return 0 spb = table[0].get('staticpush_ptr') or table[0].get('spb_ptr') or 0 if spb: return spb & 0xFFFFFFFF for e in table[:8]: spb = e.get('staticpush_ptr') or e.get('spb_ptr') if spb: return spb & 0xFFFFFFFF return 0 # Legacy fallback (GPU-id matching) def _blocks_from_face_tables_for_mesh(mk): out = [] g2k = (getattr(self, 'gpu_to_buffer_key_pos', None) or getattr(self, 'gpu_to_buffer_key_all', None) or getattr(self, 'gpu_to_buffer_key', {}) or {}) for ft in (getattr(self, "face_tables", None) or []): if not isinstance(ft, (list, tuple)) or len(ft) < 4: continue table, gpu, spb_ptr, palette_idx = ft if g2k.get(gpu) == mk: out.append({ 'table': table, 'gpu': gpu, 'spb_ptr': spb_ptr, 'palette_idx': palette_idx, }) return out # Prefer distributor output (self.pushbuffers[mk]) first def _blocks_for_mesh(mk): pbins = getattr(self, "pushbuffers", None) if isinstance(pbins, dict) and pbins.get(mk): out = [] for item in pbins[mk]: # distributor usually stores (table, palette_idx) or dict if isinstance(item, dict): out.append(item) elif isinstance(item, (list, tuple)) and len(item) >= 2: table, palette_idx = item[0], item[1] spb_ptr = item[2] if len(item) >= 3 else _staticpush_ptr_from_table(table) out.append({ 'table': table, 'gpu': item[3] if len(item) >= 4 else 0, 'spb_ptr': spb_ptr, 'palette_idx': palette_idx, }) if out: return out return _blocks_from_face_tables_for_mesh(mk) for key, d in self.mesh_data.items(): if not key.startswith(("ndVertexBuffer2_", "ndBlendShape2_")): continue verts = d.get('vertices', []) or [] tris = [] def add_strip_tris(strip, even_flip_base=0): base = len(tris) for i in range(2, len(strip)): a, b, c = strip[i - 2], strip[i - 1], strip[i] if a == b or a == c or b == c: continue if not (0 <= a < len(verts) and 0 <= b < len(verts) and 0 <= c < len(verts)): continue # flip winding every other tri if ((i + even_flip_base) & 1) == 0: tris.append((a, b, c)) else: tris.append((b, a, c)) return len(tris) - base def add_fan_tris(fan): base = len(tris) if len(fan) < 3: return 0 c = fan[0] for i in range(1, len(fan) - 1): a, b = fan[i], fan[i + 1] if c == a or c == b or a == b: continue if not (0 <= c < len(verts) and 0 <= a < len(verts) and 0 <= b < len(verts)): continue tris.append((c, a, b)) return len(tris) - base self._block_face_ranges[key] = [] blocks = _blocks_for_mesh(key) if not blocks: if not getattr(self, "_once_face_block_debug", False): self._once_face_block_debug = True print( f"[build_faces] No blocks for '{key}'. " f"pushbuffers[{key}]={len(getattr(self,'pushbuffers',{}).get(key,[]) if isinstance(getattr(self,'pushbuffers',{}),dict) else [])}, " f"face_tables={len(getattr(self,'face_tables',[]) or [])}, " f"g2k_pos={len(getattr(self,'gpu_to_buffer_key_pos',{}) or {})}, " f"g2k_all={len(getattr(self,'gpu_to_buffer_key_all',{}) or {})}" ) for meta in blocks: table = meta.get('table', []) or [] strip = [] block_start = len(tris) even_flip_base = 0 prim_mode = None # track current begin mode for entry in table: cmd = entry.get('cmd') arg = entry.get('arg', 0) payload = entry.get('payload', b"") or b"" if cmd == self.NV097_SET_BEGIN_END: prim = self.parse_set_begin_end(arg, payload) # BEGIN modes we support if prim in (self.NV097_SET_BEGIN_END_OP_TRIANGLES, self.NV097_SET_BEGIN_END_OP_TRIANGLE_STRIP, self.NV097_SET_BEGIN_END_OP_TRIANGLE_FAN): prim_mode = prim strip.clear() even_flip_base = 0 # END: flush whatever we collected elif prim == self.NV097_SET_BEGIN_END_OP_END: if strip: if prim_mode == self.NV097_SET_BEGIN_END_OP_TRIANGLE_FAN: add_fan_tris(strip) else: # TRIANGLES and STRIP both come through the same index stream here; # TRIANGLES in Conker is often encoded as strip-like batches, # so keep using strip triangulation unless you later want strict triples. add_strip_tris(strip, even_flip_base=even_flip_base) strip.clear() prim_mode = None elif cmd == self.NV097_INDEX_U16: # IMPORTANT: trust payload length, not arg bits for off in range(0, len(payload) & ~1, 2): strip.append(struct.unpack_from('<H', payload, off)[0]) elif cmd == self.NV097_INDEX_U32: # IMPORTANT: trust payload length, not arg bits for off in range(0, len(payload) & ~3, 4): strip.append(struct.unpack_from('<I', payload, off)[0]) # Table ended without END: flush if strip: if prim_mode == self.NV097_SET_BEGIN_END_OP_TRIANGLE_FAN: add_fan_tris(strip) else: add_strip_tris(strip, even_flip_base=even_flip_base) strip.clear() count = len(tris) - block_start if count > 0: spb_ptr = (meta.get('spb_ptr') or _staticpush_ptr_from_table(table)) & 0xFFFFFFFF pb_addr = 0 if hasattr(self, "_table_abs_addr"): pb_addr = self._table_abs_addr(table, spb_ptr) & 0xFFFFFFFF self._block_face_ranges[key].append({ 'pb_addr': pb_addr, 'start': block_start, 'count': count, 'spb_ptr': spb_ptr, 'shaderparam2_hint': meta.get('shaderparam2') or meta.get('sp2_ptr'), }) d['faces'] = tris print(f" '{key}' → {len(tris)} faces, {len(self._block_face_ranges[key])} blocks") total += len(tris) print(f"Total faces: {total}") def process_pushbuffer_cmds(self, block_and_label): cmds, palette_idx = block_and_label # ensure we got a proper list if not isinstance(cmds, (list, tuple)) or not cmds: return spb_ptr = cmds[0].get('staticpush_ptr', 0) if getattr(self, "_last_spb_for_ids", None) != spb_ptr: self._last_spb_for_ids = spb_ptr self.last_gpu_ids = [] # clear carry-over at SPB boundary current_gpu_ids = [] # NEW SPB: unknown until we see 0x1720 inside THIS SPB current_gpu_ids = list(getattr(self, 'last_gpu_ids', [])) current_table = [] current_block_start_abs = None for entry in cmds: cmd = entry['cmd'] arg = entry['arg'] payload = entry['payload'] sptr = entry['staticpush_ptr'] # --- 0) Debug decode hooks you already had (safe to keep) --- # if cmd == 0x0260: # NV097_SET_COMBINER_ALPHA_ICW # print(f"[Pushbuffer][0x0260] Combiner Alpha ICW at SPB 0x{sptr:08X}, payload {len(payload)} bytes:") # self.decode_nv2a_combiner_icw(payload) # continue # --- 1) Vertex buffer bind → refresh GPU IDs, reset palette state --- if cmd == self.NV097_SET_VERTEX_DATA_ARRAY_OFFSET: self._palette_active = False self._palette_idx = -1 n_ids = len(payload) // 4 current_gpu_ids = list(struct.unpack(f'<{n_ids}I', payload)) if n_ids else [] self.last_gpu_ids = current_gpu_ids continue # --- 2) Begin/End handling: prim only lives inside this block --- if cmd == self.NV097_SET_BEGIN_END: prim = self.parse_set_begin_end(arg, payload) # BEGIN (TRIANGLES/STRIP): start a new table, seed absolute PB addr immediately if prim in (self.NV097_SET_BEGIN_END_OP_TRIANGLES, self.NV097_SET_BEGIN_END_OP_TRIANGLE_STRIP, self.NV097_SET_BEGIN_END_OP_TRIANGLE_FAN): current_table = [entry] current_block_start_abs = ( entry.get('payload_abs') or entry.get('cmd_abs') or entry.get('raw_abs') or 0 ) continue # END: close the table (if open) and emit one per GPU id if prim == self.NV097_SET_BEGIN_END_OP_END and current_table: current_table.append(entry) # for gpu in (current_gpu_ids or [0]): # self.face_tables.append((current_table.copy(), gpu, sptr, palette_idx)) if current_gpu_ids: for gpu in current_gpu_ids: self.face_tables.append((current_table.copy(), gpu, sptr, palette_idx)) else: # no 0x1720 seen in this SPB → keep it "unmatched" so fallback can assign by static_ptr chain self.face_tables.append((current_table.copy(), 0xFFFFFFFF, sptr, palette_idx)) current_table = [] current_block_start_abs = 0 # ← reset for next table continue # Other BEGIN/END variants while a table is open → keep as part of the table if current_table: current_table.append(entry) if not current_block_start_abs: current_block_start_abs = ( entry.get('payload_abs') or entry.get('cmd_abs') or entry.get('raw_abs') or 0 ) continue # NEVER let 'prim' leak below this point # --- 3) Indices inside an open table --- if cmd in (self.NV097_INDEX_U16, self.NV097_INDEX_U32) and current_table: current_table.append(entry) continue # --- 4) Any other commands: if a table is open, you can keep as context (optional) --- if current_table: current_table.append(entry) if not current_block_start_abs: current_block_start_abs = ( entry.get('payload_abs') or entry.get('cmd_abs') or entry.get('raw_abs') or 0 ) continue # NEVER let 'prim' leak below this point # --- 3) Indices inside an open table --- if cmd in (self.NV097_INDEX_U16, self.NV097_INDEX_U32) and current_table: current_table.append(entry) continue # --- 4) Any other commands just pass through; if a table is open, we usually # keep them as context (optional). If you don’t want them, you can skip. --- if current_table: current_table.append(entry) if not current_block_start_abs: current_block_start_abs = ( entry.get('payload_abs') or entry.get('cmd_abs') or entry.get('raw_abs') or 0 ) These should get the maps to load now, the material issue with maps will still exist so be aware of that Edited January 20 by Über Winfrey
MrFlath Posted January 20 Posted January 20 Amazing work on everything so far, just wondering though, is it possible to create a noesis rbm import plugin?
Members Über Winfrey Posted January 21 Members Posted January 21 2 hours ago, MrFlath said: Amazing work on everything so far, just wondering though, is it possible to create a noesis rbm import plugin? I doubt it, I'm not that knowledgeable with making things for Noesis
Members Über Winfrey Posted January 22 Members Posted January 22 I did a full overhawul on the traversing, and now I'm getting the materials to show now, though there are some issues on Berri's room and the outside, I'll have to look into them to see whats bugging them
MrFlath Posted January 22 Posted January 22 You plan to upload these models when its all over? Looking for the Jugga one, that one damn
Members Über Winfrey Posted January 22 Members Posted January 22 2 hours ago, MrFlath said: You plan to upload these models when its all over? Looking for the Jugga one, that one damn the script is in the thread, you could get her yourself
Members MilesMontana Posted January 23 Members Posted January 23 On 1/21/2026 at 9:00 PM, Über Winfrey said: I did a full overhawul on the traversing, and now I'm getting the materials to show now, though there are some issues on Berri's room and the outside, I'll have to look into them to see whats bugging them Nice to see you got some progress rolling with it. Still confusing this is the one that puts up the biggest fight so far, but guess time will tell if something else comes along. Still solid work my dude.
MrFlath Posted January 25 Posted January 25 On 1/22/2026 at 9:40 AM, Über Winfrey said: the script is in the thread, you could get her yourself I couldnt, my blender is out of date and my pc sucks shit lol, I got the other one for blender 2.4 but I didnt get the damn thing to work
Members MilesMontana Posted January 26 Members Posted January 26 12 hours ago, MrFlath said: I couldnt, my blender is out of date and my pc sucks shit lol, I got the other one for blender 2.4 but I didnt get the damn thing to work Current script works for 4.0 onwards. It's not exactly impossible and pretty sure the thing can handle it if you just stick with Eevee, but I could be wrong.
Members Über Winfrey Posted January 27 Members Posted January 27 Holy shit that was a pain in the ass to fix, so the issue was that the first StaticPushBuffer was being forced onto the material builder, so during material assignment it caused misalignment, and anything that wasn't using the buffer that was used for the actual mesh drawing was tossed out I also cut out a lot of fat, so the models don't drag down performance when loading them with materials on 1
Members MilesMontana Posted January 28 Members Posted January 28 19 hours ago, Über Winfrey said: Holy shit that was a pain in the ass to fix, so the issue was that the first StaticPushBuffer was being forced onto the material builder, so during material assignment it caused misalignment, and anything that wasn't using the buffer that was used for the actual mesh drawing was tossed out I also cut out a lot of fat, so the models don't drag down performance when loading them with materials on I'd believe it on the pain in the ass part. Congrats though on figuring it out and for finding a way to trim down performance issues.
Pooka_Hobgoblin Posted February 5 Posted February 5 On 12/29/2025 at 9:10 PM, MilesMontana said: Gotta love Windows bullshit. NVM, I got it to work. Some weird shit went on. I've been trying for a bit to find anything on how I could get this program to work properly. Running win 11 and getting no results from the process. I followed along a bit earlier in the thread and was able to get "default.rbm.unpacked", I just keep bashing into the wall of "Access is denied." I am illiterate unlike the previous individual, but a great desire to learn.
Members MilesMontana Posted February 5 Members Posted February 5 13 hours ago, Pooka_Hobgoblin said: I've been trying for a bit to find anything on how I could get this program to work properly. Running win 11 and getting no results from the process. I followed along a bit earlier in the thread and was able to get "default.rbm.unpacked", I just keep bashing into the wall of "Access is denied." I am illiterate unlike the previous individual, but a great desire to learn. Okay, I might know the issue here. So,if you want to rip these things open, try this instead: .Open command prompt, run in administrator mode in case since you know how Windows likes to be .(Optional if you have other drives) Type the name of your drive like so: D: or whatever name it is followed by the colon. .Type "cd "insert location of the decompiled game and the file you're trying to get here", but only the folder. Not the exact file just yet. .Type clr_unpack default.rbm -d index All you needed to do there was just not write the .exe part because it's weird that way. If done right, you should get a .mapped file out of it. Hope this helps! 1
Pooka_Hobgoblin Posted February 7 Posted February 7 On 2/5/2026 at 2:32 PM, MilesMontana said: Okay, I might know the issue here. So,if you want to rip these things open, try this instead: .Open command prompt, run in administrator mode in case since you know how Windows likes to be .(Optional if you have other drives) Type the name of your drive like so: D: or whatever name it is followed by the colon. .Type "cd "insert location of the decompiled game and the file you're trying to get here", but only the folder. Not the exact file just yet. .Type clr_unpack default.rbm -d index All you needed to do there was just not write the .exe part because it's weird that way. If done right, you should get a .mapped file out of it. Hope this helps! It worked like a charm! Thank you for taking the time to help me with this. super stoked to have a proper reference for a later physical project I'd like to build 1
Members MilesMontana Posted February 7 Members Posted February 7 10 hours ago, Pooka_Hobgoblin said: It worked like a charm! Thank you for taking the time to help me with this. super stoked to have a proper reference for a later physical project I'd like to build Happy to help. It took me a bit to process how to do it myself as well the first time around, but the results are pretty good. Only level you might not have a good time with rn is the Beach running section since the current script doesn't have it fixed up, but the code was given for tweaking it. Otherwise, good luck with your project.
Pooka_Hobgoblin Posted February 7 Posted February 7 1 hour ago, MilesMontana said: Happy to help. It took me a bit to process how to do it myself as well the first time around, but the results are pretty good. Only level you might not have a good time with rn is the Beach running section since the current script doesn't have it fixed up, but the code was given for tweaking it. Otherwise, good luck with your project. I believe I saw the blanked sections as individual objects (Like the barb wire barrier) It would make sense they are swapped out when obscured by the explosion Thank you and everyone for this amazing project 1
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now