don't click here

Kilo's Misadventures in Ripping Goldeneye Models (And VR Dev)

Discussion in 'Technical Discussion' started by Kilo, May 30, 2024.

  1. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Tech Member
    542
    544
    93
    Canada
    S1 - Metal Sonic's Challenge, Sonic 1 Rev01 ASMX Disasm
    So I love Goldeneye 007 for the N64. It's a game that makes you really feel like James Bond, even 27 years after the fact. Which is impressive considering you're holding this alien monstrosity.
    [​IMG]
    And if this can make you feel like James Bond, imagine how much better it'd be in VR. So that's a project I set out to do. I didn't intend to port the whole game, all I needed was to be able to walk around Dam or Facility and shoot at guards, I didn't care if there were missions or every single weapon. I just wanted to experience Goldeneye in VR, along with it being a tool to learn both VR development and furthering my knowledge of the Godot engine.

    So the first thing I needed, the absolute minimum, was Dam's model. Easy enough, I'll just head to the Model's Resource.
    upload_2024-5-29_22-10-14.png
    Well that's some models... Not the ones I need though. There's Citadel, a cut level that I could use, but it's unfinished having 1 sided faces that would probably be disorientating in VR, and I have no childhood attachment to it anyways.

    Alright, no problem, let's do some more digging. Ah! A Goldeneye decompilation, perfect!
    upload_2024-5-29_22-13-35.png
    Hrm, not an issue. I do keep a totally legit and purchased copy of the game on me at all times anywhere I go. Let's extract it. I just have to run extract_baserom.u.sh. Oh, Windows can't natively run bash files. So I look up how I can run it, I figured bash was just a component you can install on Windows, but I suppose not, I'm pointed to enabling Windows subsystem for Linux. Alright, let's run ba- Oh I need to actually install a distro alright. Finally I can extract the files from the ROM!

    I run the script and-
    upload_2024-5-29_22-23-48.png
    Why are you skipping assets.

    Well, I at least have most of the assets extracted, now let's open them. I root around in the tools folder... Nothing.

    Alright, how about we look at the modding tools for this game, I know that there are totally custom levels so the tools have to be advanced. I do some research, and learn of Goldeneye Setup Editor. Click the link... It's been claimed by some RPG tabletop guide thing. Ok, I'll just look it up myself, thankfully it has an archive. Give it an install, and it turns out the editor came with all the game assets so messing with extracting files was just a waste of my time. Alright I'll load up a level, and there we go!
    upload_2024-5-29_22-40-58.png
    ... Ok so it has the option to import .obj files as level files. But you can't export. Lovely.

    Fine if fan tools aren't going to help me, then I'll go to the official tools. N64 models were made with something called Ningen, which is a modified version of ModelGen, a piece of 3D modeling software from 1994. It just so happens that in 2020 a version was found and archived. I figured it was worth a shot. So I download it, realize it's a unix based program, I suppose that makes sense for 94, thankfully thanks to the bash shenanigans I now had wsl and kali installed so that works out. I didn't expect it to work, but I run the installer and somehow it did in fact work. But trying to execute the actual program... It's format wasn't compatible with modern Linux. Fair. At this point I'm tired. I wonder if this is even worth it, how did the people on the Model's Resource do this, and why didn't they export more important models than a random floppy disk model.

    I do a bit more digging and found something called "GEOBJ Plugin for Blender" it only works with Blender 2.78 but it's SOMETHING. Blender at least keeps it's historical version so I pulled it up easily. I'm initially confused because after importing the plugin it didn't automatically show up as an import option, but after navigating to the users settings page to enable the plugin I finally try and import a model aaand... Nothing.
    upload_2024-5-29_23-9-22.png

    In conclusion; I wasted 4 hours and I'm going to bed.

    I have a few more ideas on how to do this, but I need to rest. I just wanted to share part of the reverse engineering process, how often you'll be running in circles, and the thought process behind doing the things you need to.

    I'll keep this thread updated on my efforts to get these damn models, and how the VR stuff goes, assuming I don't pull my hair out.
     
    • Like Like x 2
    • Useful Useful x 1
    • List
  2. Bobblen

    Bobblen

    Member
    403
    206
    43
    Admittedly I was just curious and used the setup editor for all of a few minutes but in the visual editor, I just right clicked and one of the options is 'output to obj' with a bunch of sub types to choose from? I quickly tried it out with 'output object models to obj' and was able to import the obj to blender. This was with version 3.0 btw. Maybe I'm missing something about what you're trying to achieve, but just in case, thought I throw this up here.

    It's really cool that people put in the time to create such a full featured editor for an N64 game, I'd never seen it before.

    EDIT, the right click menu is context sensitive. If you change to 'edit room' mode you can export the mesh for the rooms as an obj file. There's also an 'export whole level to obj' option as well. I was in 'edit object' mode before so was only getting object related choices.
     
    Last edited: May 30, 2024
  3. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Tech Member
    542
    544
    93
    Canada
    S1 - Metal Sonic's Challenge, Sonic 1 Rev01 ASMX Disasm
    You sir, are my saviour.
    upload_2024-5-30_6-16-58.png

    I figured if this button was going to be anywhere it would've been the convert tab. I guess not.
    The problem was that the help tab that probably would've told me this links to that dead site I was talking about.
     
  4. Bobblen

    Bobblen

    Member
    403
    206
    43
    Looks great! Good luck with your project.
     
  5. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Tech Member
    542
    544
    93
    Canada
    S1 - Metal Sonic's Challenge, Sonic 1 Rev01 ASMX Disasm
    I've also sorted out that you can export collision models the same way. Although in GE, collision only means the areas you're able to stand on, so effectively there's only floor collision and no wall collision, if there's no floor you simply can't go there. But it's the minimum I needed to at least make a walking sim.
    ezgif-3-868df5b5d2.gif
    (Also the forum should really upgrade the limit on GIF sizes, 1 MB really isn't enough anymore)

    Z-fighting's also pretty bad here (Not really visible in the GIF due to the low framerate). Can't really think of a solution that wouldn't involve going through and modifying the mesh to prevent cases of it. I guess that's one of the advantages of a BSP tree.

    Edit: Alright, so I've added the actual VR part of this. It was shockingly easy to implement. Godot's engine really does all the heavy lifting for devs. And it only required 3 lines of code to tell the game which VR interface to use. But otherwise it's entirely handled by nodes. Very impressive! Went and tore off Ourumov's hands from his model to serve as the hand models here, he won't be needing them. :)
    ezgif-1-4ac727ecbc.gif
     
    Last edited: May 31, 2024
    • Like Like x 1
    • Useful Useful x 1
    • List
  6. Chimes

    Chimes

    The One SSG-EG Maniac Member
    754
    543
    93
    This is what people in the 90s thought the future of video games would look like. This is stuff straight out of that DisneyQuest promo lol
    Genuinely surprised GoldenEye tools are in this state though. I was digging through how to extract files out of GCM and had to use 21 year old programs...
     
  7. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Tech Member
    542
    544
    93
    Canada
    S1 - Metal Sonic's Challenge, Sonic 1 Rev01 ASMX Disasm
    Well, turns out I'm not quite done with ripping.
    .obj Does not support vertex colouring. Which is a huge issue since early 3D games, especially Goldeneye use vertex colouring to get around excessive texture usage, or even to avoid implementing lighting. Examples:
    The rust on Dam's wall. As well as even the water in the dam.
    upload_2024-6-1_16-44-31.png
    upload_2024-6-1_16-45-14.png

    And the tunnels underneath the Dam
    upload_2024-6-1_16-48-15.png
    upload_2024-6-1_16-48-48.png

    I could go on without the use of vertex colours, and just use baked lighting, since it is 2024 and not 1997. But the entire point is to feel like I'm sucked into my N64.

    Thankfully GoldenEditor does support exporting to more than just .obj. We have .fbx and .dae. We'll try .dae first.
    upload_2024-6-1_16-55-24.png
    Alright, this is odd. So none of the actual textures are here, just the vertices, and areas where overlay textures would be used (You can mostly see it in the tunnels since they use a lot of "moss" overlays) seem to cast shadows? Well I don't have time to deal with this, so let's try using .fbx. To my understanding .fbx is one of the more preferrable formats for 3D models, so it should do us better.
    upload_2024-6-1_17-1-10.png
    Ah hrm. Okay, well something out there has to support ASCII FBX, otherwise it wouldn't exist! Let's see if we can just directly drop this into Godot.
    upload_2024-6-1_17-4-28.png
    Well that worked, we have vertex colours, but no textures.
    There's no winning here, is there? Well think again. If we just head over to the mesh's import options, and onto the materials tab
    upload_2024-6-1_17-6-44.png
    All these materials share the same names as the textures that got dumped when the model was exported. They just somehow didn't get associated with the materials. So, under the actions button, we can rip these materials, have the mesh use the ripped ones, and then associate our textures with the ripped mats.

    So 105 materials later: And we've got a vertex coloured map. Not ideal that I had to do it by hand.
    upload_2024-6-1_19-7-52.png
    At the very least, combing through each material at least allowed me to make sure transparent textures such as the hand rails had the right culling applied.
     
  8. Bobblen

    Bobblen

    Member
    403
    206
    43
    Wow, you're really going for it with this one, I'd have probably called it a day with the textures and models and used modern lighting :-D
     
  9. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Tech Member
    542
    544
    93
    Canada
    S1 - Metal Sonic's Challenge, Sonic 1 Rev01 ASMX Disasm
    Today's matter; Water
    When importing, you only get static textures, this makes sense.
    upload_2024-6-2_18-11-1.png
    However water in Goldeneye, is not static.
    ezgif-1-97ab276dfa.gif
    It seems to me there are 2 effects going on, a scroll which is obvious, and a more subdued effect of the water texture fading in and out.
    So how can we achieve this in Godot? Why with shaders, of course. First we'll make the material that takes our shader, and call it water of course, and apply it to the level mesh.
    upload_2024-6-2_18-13-11.png
    Then we'll make the actual shader, and apply it to the material. And now we can start working.
    upload_2024-6-2_18-22-4.png
    So this is our shader to make it scroll, here's a rundown of what it does. It takes time, and converts it into X/Y coords (We negate the X coord so it scrolls in the proper direction). It then divides by 6 just to slow it down, plugs that into a UV panning function to get the texture's offset, and then applies the texture. Which then gives us this
    ezgif-1-a4c5292939.gif
    Great so we've got scrolling, but now we've lost our vertex colours again from the obj/fbx hassle! Well let's see what we can do.
    Godot offers vertex colour as a shader input, so in theory if we add that then we should be good to go.
    upload_2024-6-2_18-30-28.png
    It's key that we overlay the texture onto the vertex colour and not the other way around, but with that we get this:
    ezgif-1-80bcdadbc5.gif
    Not quite as dark as it appears in game, well why is that. Well frankly, I'm not quite too sure. Level configurations do have a water colour parameter, but Dam doesn't seem to actually use it since changing the water to pure red did nothing. We'll sort it out later. But for now, let's add the fading effect. I'm just gonna do a quick fix for today, since I want to move onto other things. But we'll throw the time into a sine function, clamp it to 0.5 so the water doesn't fully disappear and plug that into the alpha of the material.
    upload_2024-6-2_19-14-6.png
    ezgif-2-6236d98054.gif
    And we'll call it quits here for today. While it's not 100% N64 accurate like I'm going for it at least gets the basics of what I'm trying to do in. Better to have miscoloured animated water than just miscoloured water. This was also my first time messing around with shaders, frankly not as scary as I made them out to be when I used to develop on Unity.

    Edit: Finally got the shader down.
    upload_2024-6-3_10-9-51.png
    I'm not even going to start to try and explain this. And I don't want to touch it. It's N64 accurate and that's all I want.
     
    Last edited: Jun 3, 2024
  10. Chimes

    Chimes

    The One SSG-EG Maniac Member
    754
    543
    93
    Make sure to save that as a backup, that kind of fragile black box tinkering is how stuff gets lost with one single change
     
  11. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Tech Member
    542
    544
    93
    Canada
    S1 - Metal Sonic's Challenge, Sonic 1 Rev01 ASMX Disasm
    Today's matter, Z-fighting. An issue I've been fighting for the past week since I started this project.
    So what's Z-fighting? It's pretty simple, when vertices are in the same place, they 'fight' for priority often resulting in glitchy looking visuals. For instance, it's pretty bad looking at the edges of the docks on Dam.
    upload_2024-6-4_18-53-8.png
    For reference on the N64 the grime texture appears to be overlayed on top of the stone.
    upload_2024-6-4_18-57-24.png
    Typically the fact that one texture takes priority over the other when they share the same position it's because there's a lack of a depth buffer, or the use of a custom depth buffer. But the N64 famously has a depth buffer, one of the advantages it had over the PS1 (On top of having an FPU, trilinear texture filtering, etc) so how did Goldeneye do this? Well it goes back to Doom (When does it not). Levels in Goldeneye are built with BSP trees, much like Doom- actually more like Quake, Doom's BSPs weren't nearly this advanced, but shhh saying it's related to Doom sounds cooler, and one of the advantages of that is that since it's a tree, you can set vertexes to be rendered after it's parent, and that's GE gets around Z-fighting.

    Now we're not currently using BSPs, nor do I intend to implement a BSP renderer, when we just spent a good few hours figuring out how to export the level into a standardized model format. So we're just going to figure out how to make the material render above other vertexes. This will be our guinea pig for this episode seeing as the grime texture could be difficult to distinguish from the base material it's overlapping, a nice clear 2 will make it evident what our changes are doing.
    upload_2024-6-4_19-39-8.png

    My initial thought was to use a shader. It wasn't a bad idea, I already dipped my toes into the water with shaders in our previous 'episode' on water, so why not delve deeper into that realm. The approach we were going to use was to get the vertex's normal (Forward direction) and just move it in that direction by a small amount to move the overlay vertex out of the conflicting vertex. This was the right thought, but we'll soon find out trying to make a new shader was a waste of time. I then come to find that Godot's visual shaders don't have nodes for modifying the vertex's position, or if it does, then I couldn't find it. Not to fret, instead of using visual shaders which are for BABIES that can't code like MEN, we'll instead learn Godot's shader language. So I figured it was just as simple as this.
    Code (Text):
    1. void vertex() {
    2.     POSITION = vec4(VERTEX +(NORMAL * 0.1), 1.0);
    3. }
    It was not.
    I continue to mess around with this... And somehow I generated an egg inside the material, which was amusing.
    [​IMG]
    So I kind of give up on this approach. And decided to look at the root of the issue, the depth buffer. And like any frustrated lazy programmer, I resort to ChatGPT. And it told me to just do this to give the material priority in the depth buffer.
    Code (Text):
    1. void fragment() {
    2.     DEPTH = -1.0;
    3. }
    Still no go. At this point, I give up for the day, and play Fallout New Vegas on call with friends until 4:30 in the morning with my kidneys sobbing from excessive Monster energy drink intake.

    Coming back the next day, I decided to further look into the options provided to me by Godot's standard spatial shader. The first thing I decided to try was the "No depth test" toggle. Which did get the material in front of the conflicting vertex...
    upload_2024-6-4_19-40-1.png
    But it also put it in front of EVERY vertex.
    upload_2024-6-4_19-40-38.png
    Then as I was browsing Godot's standard material docs... I saw it.
    upload_2024-6-4_19-41-20.png
    This was exactly what I was trying to do with the shader earlier, right there in the bog standard material! So we just apply that to our 2 material here.
    upload_2024-6-4_19-44-24.png
    And now we've got Z-fighting solved without the material going in front of everything. Now to apply it to the whole level (Oh also clouds! Nothing to say much about, they use the same shader as water), and it now looks identical to the N64!
    upload_2024-6-4_19-55-23.png

    Now I can finally focus on the gameplay part of this.

    Edit; by the way check out this goofy edit I made for the game's icon.
    upload_2024-6-5_12-14-41.png
     
    Last edited: Jun 5, 2024
  12. Clownacy

    Clownacy

    Tech Member
    1,076
    638
    93
    Nice to see someone making big technical posts! 3D stuff is still pretty alien to me, so this has been interesting to read. By the way, the possessive form of 'it' is 'its', not 'it's'.
     
  13. Cooljerk

    Cooljerk

    Professional Electromancer Oldbie
    4,608
    284
    63
    I remade the dam level of goldeneye in UE4 back in 2015 with the HTC Vive, mimicing the controls of the then brand-new Budget Cuts. Fun project to do. My goal was the opposite, however -- it was less on leaning how to rip goldeneye assets and models, and more about trying to clone budget cuts teleportation mechanic and throw it into an existing map. I just remade the dam level by eyeballing it.
     
  14. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Tech Member
    542
    544
    93
    Canada
    S1 - Metal Sonic's Challenge, Sonic 1 Rev01 ASMX Disasm
    Alright, let's get into controls! Typically engines like Godot and Unity have an "input system" as they often refer to it. This allows the developer to bind controller buttons and keyboard keys to actions which can easily be referenced from any script anywhere in the project, and is always running as a core component of the engine. VR controls, unfortunately are not that simple, but it's not terrible either, in Godot at least.

    First let's discuss what's processing our inputs if the input system won't do it for us. In my 3rd post on this thread I demonstrated the blocky hand models, to track to the player's hand movements. Those used a Godot node called XR Controllers, and along with obviously providing hand tracking these will serve as our input drivers.

    And if our input bindings from the main input system can't be used, what's the VR equivalent? Action maps. They comprise of action sets, and profiles. Action sets define our possible actions and their value types such as bools for buttons, floats for triggers, and vectors for sticks, For example a shoot action button with a bool type. Then profiles are used to map those actions on the actual buttons of individual controllers. Godot offers a decent chunk of them:
    upload_2024-6-13_17-54-0.png
    However I'm targeting a Meta Quest 2, which seems to have been an option in older versions of Godot but has since been removed. So we'll instead use the OpenXR fallback type, touch controller. The Meta Quest 2 does have capacitive buttons that can detect when you're just touching and not pressing a button, which is why I'm not instead using the simple controller type, it may come in handy later, who knows, at least we'll have it. Also worth noting that by mapping the controllers, it for some reason disables the controller tracking. The reason for this is odd, to say. And that's because when you create a new XR Controller node it has a base pose input, which by default, is well, default. However "default" is actually a deprecated term, and it realizes it's deprecated when you map the controllers, I guess? I just had to switch it to "aim" which solved it but didn't seem to change anything. So ¯\_(ツ)_/¯

    So how do we tell our scripts to use these actions then? Since it's being handled by the OpenXR Controller nodes surely we have to reference them constantly and make a mess. Wrong! Nodes in Godot often offer something Signals, which usually indicate a change in the node, in this case with the controllers, button presses and analogue changes. So we can connect Signals to a script and that allows us to generate a method that will be called whenever that signal gets broadcasted. For now since I'm just tackling basic movement we'll only use 2 signals, that being those of the left and right sticks for moving and rotating.

    And after a little bit of work:
    ezgif-4-edb69af25f.gif
    Right now it's very very basic. I'll implement camera relative movement, Goldeneye's stand system, see:
    And the small amount of momentum the original had later. This was mostly just to discuss what I learned about VR input rather than movement.