Jump to content

[Xbox] Conker Live and Reloaded models


Recommended Posts

  • Members
Posted

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
Posted
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.

image.thumb.png.ff9fdc38164f7cbc17f65177451179e7.pngthe textures are importing, it just looks like a bug with the material builder, which I'll ahve to figure out

  • Members
Posted
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
Posted
2 hours ago, Über Winfrey said:

I haven't, only time I looked at them was when the script only imported the diffuse texture

image.thumb.png.a987c094f0c37373cd327121e4de5778.pngYeah, not exactly a pretty sight. Oddly more missing than expected.

 

  • Members
Posted (edited)
8 hours ago, MilesMontana said:

image.thumb.png.a987c094f0c37373cd327121e4de5778.pngYeah, 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 by Über Winfrey
  • Members
Posted

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
Posted (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, whoopsimage.thumb.png.05b3df64c021152a5bd380410908a6aa.png

Edited by Über Winfrey
  • Like 2
  • Members
Posted (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 by Über Winfrey
  • Members
Posted
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
Posted

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

image.thumb.png.8085c3b2a120d1b10a4eeee7184a3dbe.png

  • Members
Posted
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
Posted
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

image.thumb.png.8085c3b2a120d1b10a4eeee7184a3dbe.png

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. 

Posted
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
Posted
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
Posted

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
image.thumb.png.a0d94a67c5f79b58ea89fd1235e51e6a.pngimage.thumb.png.37765b3ca368b4b4bedb2fedd3f67b55.png

I also cut out a lot of fat, so the models don't drag down performance when loading them with materials on

  • Like 1
  • Members
Posted
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
image.thumb.png.a0d94a67c5f79b58ea89fd1235e51e6a.pngimage.thumb.png.37765b3ca368b4b4bedb2fedd3f67b55.png

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.

  • 2 weeks later...
Posted
On 12/29/2025 at 9:10 PM, MilesMontana said:

Gotta love Windows bullshit.
image.png.0a46efc559da1b57cf42d960e8d4f5e5.pngimage.png.b6f85113d07a0148e5f67938b9a7c0ef.png

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.

Screenshot 2026-02-05 010751.png

Screenshot 2026-02-05 010804.png

  • Members
Posted
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.

Screenshot 2026-02-05 010751.png

Screenshot 2026-02-05 010804.png

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!

  • Like 1
Posted
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

Screenshot 2026-02-07 003240.png

  • Like 1
  • Members
Posted
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

Screenshot 2026-02-07 003240.png

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.

Posted
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

  • Like 1

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...