mirror of
https://github.com/open-goal/jak-project.git
synced 2024-11-23 06:09:57 +00:00
custom levels: support vanilla skies and texture remapping tables (#3691)
This adds some new JSON entries to custom levels so they can support vanilla sky textures and the texture remapping tables, allowing for proper textures on objects that use `generic`, like dark eco pools or dying enemies. The comments explain it in more detail, but the gist is: For skies: - `sky` needs to be a vanilla level that has sky textures. - The alpha tpage (fourth entry in `tpages`) needs to be that vanilla level's alpha tpage (if `tex_remap` is the same level as `sky`, this will be handled automatically). - The tpage needs to be added to the custom level `.gd` and to `textures` in the JSON. - In `level-info.gc`, `sky` needs to be `#t`, your level's mood needs to call `update-mood-sky-texture` (the default mood, `update-mood-default`, handles this as an example) and `sun-fade` needs to be nonzero for the sun to show up. For `generic` textures: - `tex_remap` needs to be the name of a vanilla level. - When using a vanilla level's remap table, you need to adhere to the order of the files in that level's `.gd` in your own level. - Code files are first. - Then the tpages (in the order `tfrag`, `pris`, `shrub`, `alpha`, `water`). - Then the art groups. - Lastly, the level file. - The tpages need to be added to the `textures` in the JSON.
This commit is contained in:
parent
fedfb6fd09
commit
614c5a663c
@ -67,21 +67,60 @@
|
||||
"custom_models": ["test-actor"],
|
||||
|
||||
// Any textures you want to include in your custom level. This is mainly useful for things such as the zoomer HUD,
|
||||
// which is not in the common level files and has no art group associated with it.
|
||||
// sky textures and cases where you want to use objects drawn with the generic renderer,
|
||||
// which also requires you to set a texture remap table and a list of tpages.
|
||||
// To get a list of all the textures, you can extract all of the game's textures
|
||||
// by setting "save_texture_pngs" to true in the decompiler config.
|
||||
// The format is ["tpage-name", "texture-name1", "texture-name2", ...].
|
||||
// If you want all textures from a tpage, you can just do ["tpage-name"].
|
||||
"textures": [
|
||||
// all textures required for the zoomer HUD
|
||||
// "zoomerhud",
|
||||
// "zoomerhud-dial",
|
||||
// "zoomerhud-main-02",
|
||||
// "zoomerhud-main-03",
|
||||
// "zoomerhud-pieslice",
|
||||
// "zoomerhud-heatbg-01",
|
||||
// "zoomerhud-main-03arrow",
|
||||
// "zoomerhud-main-03knob"
|
||||
// Zoomer HUD textures
|
||||
// ["zoomerhud"],
|
||||
// Sandover sky textures
|
||||
["village1-vis-alpha"]
|
||||
],
|
||||
|
||||
// Which vanilla level's texture remap table to use.
|
||||
// This is needed if you want to have proper textures for objects that are drawn using generic, such as
|
||||
// dark eco pools or dying enemies. If you choose to use this,
|
||||
// the order of art groups and tpages in your level's .gd MUST match the vanilla level's for the textures to work.
|
||||
"tex_remap": "village1",
|
||||
|
||||
// Which sky to use for your custom level. This needs to be the name of a vanilla level with sky textures.
|
||||
// Your level's mood update needs to be set up to copy the sky texture (update-mood-default already handles this as an example).
|
||||
// You also need to add a specific tpage to your level .gd depending on the level you chose:
|
||||
// training: tpage-1308.go
|
||||
// village1: tpage-401.go
|
||||
// beach: tpage-215.go
|
||||
// jungle: tpage-388.go
|
||||
// misty: tpage-520.go
|
||||
// firecanyon: tpage-1123.go
|
||||
// village2: tpage-921.go
|
||||
// rolling: tpage-925.go
|
||||
// sunkenb: tpage-162.go
|
||||
// swamp: tpage-630.go
|
||||
// ogre: tpage-1117.go
|
||||
// village3: tpage-1194.go
|
||||
// snow: tpage-712.go
|
||||
// finalboss: tpage-1418.go
|
||||
// The tpage you choose also has to be in the alpha (4th) slot in the "tpages" list below.
|
||||
"sky": "village1",
|
||||
|
||||
// Any texture pages that are added to the level's "texture-ids" array.
|
||||
// These will be logged in and linked as part of level loading.
|
||||
// There are five tpage categories, in this order:
|
||||
// - tfrag
|
||||
// - pris
|
||||
// - shrub
|
||||
// - alpha
|
||||
// - water
|
||||
// This is also (with the exception of levels that use the zoomer) the order that the tpages are listed in in the vanilla .gd files.
|
||||
// You need to also add these to your level .gd as well.
|
||||
// Sky textures use the alpha slot.
|
||||
// If this is empty and "tex_remap" and "sky" are the same level, this will be auto-filled with the tpages from the
|
||||
// corresponding level (check its .gd file for the art group/tpage order to copy over).
|
||||
"tpages": [],
|
||||
|
||||
// Ambients you want to use in your custom level. Ambients can be used for various things like changing the music variation (flava),
|
||||
// adding ambient sounds, level name hints, etc.
|
||||
"ambients": [
|
||||
@ -97,10 +136,10 @@
|
||||
}
|
||||
],
|
||||
|
||||
"actors" : [
|
||||
"actors": [
|
||||
{
|
||||
"trans": [-21.6238, 20.0496, 17.1191], // translation
|
||||
"etype": "fuel-cell", // actor type
|
||||
"etype": "fuel-cell", // actor type
|
||||
"game_task": "(game-task none)", // associated game task (for powercells, etc)
|
||||
"quat": [0, 0, 0, 1], // quaternion
|
||||
"bsphere": [-21.6238, 19.3496, 17.1191, 10], // bounding sphere
|
||||
@ -111,11 +150,11 @@
|
||||
},
|
||||
|
||||
{
|
||||
"trans": [-15.2818, 15.2461, 17.1360], // translation
|
||||
"etype": "crate", // actor type
|
||||
"trans": [-15.2818, 15.2461, 17.136], // translation
|
||||
"etype": "crate", // actor type
|
||||
"game_task": "(game-task none)", // associated game task (for powercells, etc)
|
||||
"quat": [0, 0, 0, 1], // quaternion
|
||||
"bsphere": [-15.2818, 15.2461, 17.1360, 10], // bounding sphere
|
||||
"bsphere": [-15.2818, 15.2461, 17.136, 10], // bounding sphere
|
||||
"lump": {
|
||||
"name": "test-crate",
|
||||
"crate-type": "'steel",
|
||||
@ -124,18 +163,18 @@
|
||||
},
|
||||
|
||||
{
|
||||
"trans": [-5.4630, 17.4553, 1.6169], // translation
|
||||
"etype": "eco-yellow", // actor type
|
||||
"trans": [-5.463, 17.4553, 1.6169], // translation
|
||||
"etype": "eco-yellow", // actor type
|
||||
"game_task": "(game-task none)", // associated game task (for powercells, etc)
|
||||
"quat": [0, 0, 0, 1], // quaternion
|
||||
"bsphere": [-5.4630, 17.4553, 1.6169, 10], // bounding sphere
|
||||
"bsphere": [-5.463, 17.4553, 1.6169, 10], // bounding sphere
|
||||
"lump": {
|
||||
"name": "test-eco"
|
||||
}
|
||||
},
|
||||
{
|
||||
"trans": [-5.41, 3.5, 28.42], // translation
|
||||
"etype": "test-actor", // actor type
|
||||
"etype": "test-actor", // actor type
|
||||
"game_task": 0, // associated game task (for powercells, etc)
|
||||
"quat": [0, 0, 0, 1], // quaternion
|
||||
"bsphere": [-7.41, 3.5, 28.42, 10], // bounding sphere
|
||||
@ -154,4 +193,4 @@
|
||||
// }
|
||||
// }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,14 @@
|
||||
|
||||
;; the actual file name still needs to be 8.3
|
||||
("TSZ.DGO"
|
||||
("static-screen.o"
|
||||
"test-zone-obs.o"
|
||||
("test-zone-obs.o"
|
||||
"tpage-398.go"
|
||||
"tpage-400.go"
|
||||
"tpage-399.go"
|
||||
"tpage-401.go"
|
||||
"tpage-1470.go"
|
||||
"plat-ag.go"
|
||||
"test-actor-ag.go"
|
||||
"test-zone.go"
|
||||
))
|
||||
)
|
||||
)
|
@ -49,27 +49,29 @@
|
||||
// adds a 'type' tag (using the "symbol" and "string" lump types works the same way):
|
||||
// "spawn-types": ["type", "spyder", "juicer"]
|
||||
|
||||
// The base actor id for your custom level. If you have multiple levels, this should be unique!
|
||||
"base_id": 100,
|
||||
// The base actor id for your custom level. If you have multiple levels, this should be unique!
|
||||
"base_id": 100,
|
||||
|
||||
// All art groups you want to use in your custom level. Will add their models and corresponding textures to the FR3 file.
|
||||
// Removed so that the release builds don't have to double-decompile the game
|
||||
// "art_groups": ["prsn-torture-ag"],
|
||||
|
||||
// Any textures you want to include in your custom level.
|
||||
// This is mainly useful for textures which are not in the common level files and have no art group associated with them.
|
||||
// To get a list of all the textures, you can extract all of the game's textures
|
||||
// by setting "save_texture_pngs" to true in the decompiler config.
|
||||
"textures": [],
|
||||
// Any textures you want to include in your custom level.
|
||||
// This is mainly useful for textures which are not in the common level files and have no art group associated with them.
|
||||
// To get a list of all the textures, you can extract all of the game's textures
|
||||
// by setting "save_texture_pngs" to true in the decompiler config.
|
||||
// The format is ["tpage-name", "texture-name1", "texture-name2", ...].
|
||||
// If you want all textures from a tpage, you can just do ["tpage-name"].
|
||||
"textures": [],
|
||||
|
||||
"actors" : [
|
||||
"actors": [
|
||||
{
|
||||
"trans": [-15.2818, 15.2461, 17.1360], // translation
|
||||
"etype": "crate", // actor type
|
||||
"trans": [-15.2818, 15.2461, 17.136], // translation
|
||||
"etype": "crate", // actor type
|
||||
"game_task": 0, // associated game task (for powercells, etc)
|
||||
"kill_mask": 0,
|
||||
"quat": [0, 0, 0, 1], // quaternion
|
||||
"bsphere": [-15.2818, 15.2461, 17.1360, 10], // bounding sphere
|
||||
"bsphere": [-15.2818, 15.2461, 17.136, 10], // bounding sphere
|
||||
"lump": {
|
||||
"name": "test-crate",
|
||||
"eco-info": ["eco-info", "(pickup-type health)", 2]
|
||||
@ -77,12 +79,12 @@
|
||||
},
|
||||
|
||||
{
|
||||
"trans": [-5.4630, 17.4553, 1.6169], // translation
|
||||
"etype": "eco-yellow", // actor type
|
||||
"trans": [-5.463, 17.4553, 1.6169], // translation
|
||||
"etype": "eco-yellow", // actor type
|
||||
"game_task": "(game-task none)", // associated game task (for powercells, etc)
|
||||
"kill_mask": 0,
|
||||
"quat": [0, 0, 0, 1], // quaternion
|
||||
"bsphere": [-5.4630, 17.4553, 1.6169, 10], // bounding sphere
|
||||
"bsphere": [-5.463, 17.4553, 1.6169, 10], // bounding sphere
|
||||
"lump": {
|
||||
"name": "test-eco"
|
||||
}
|
||||
@ -90,7 +92,7 @@
|
||||
|
||||
{
|
||||
"trans": [-7.41, 13.5, 28.42], // translation
|
||||
"etype": "prsn-torture", // actor type
|
||||
"etype": "prsn-torture", // actor type
|
||||
"game_task": "(game-task none)", // associated game task (for powercells, etc)
|
||||
"quat": [0, 0, 0, 1], // quaternion
|
||||
"bsphere": [-7.41, 13.5, 28.42, 10], // bounding sphere
|
||||
@ -99,4 +101,4 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -49,26 +49,28 @@
|
||||
// adds a 'type' tag (using the "symbol" and "string" lump types works the same way):
|
||||
// "spawn-types": ["type", "spyder", "juicer"]
|
||||
|
||||
// The base actor id for your custom level. If you have multiple levels, this should be unique!
|
||||
"base_id": 100,
|
||||
// The base actor id for your custom level. If you have multiple levels, this should be unique!
|
||||
"base_id": 100,
|
||||
|
||||
// All art groups you want to use in your custom level. Will add their models and corresponding textures to the FR3 file.
|
||||
// "art_groups": [],
|
||||
|
||||
// Any textures you want to include in your custom level.
|
||||
// This is mainly useful for textures which are not in the common level files and have no art group associated with them.
|
||||
// To get a list of all the textures, you can extract all of the game's textures
|
||||
// by setting "save_texture_pngs" to true in the decompiler config.
|
||||
// "textures": [],
|
||||
// Any textures you want to include in your custom level.
|
||||
// This is mainly useful for textures which are not in the common level files and have no art group associated with them.
|
||||
// To get a list of all the textures, you can extract all of the game's textures
|
||||
// by setting "save_texture_pngs" to true in the decompiler config.
|
||||
// The format is ["tpage-name", "texture-name1", "texture-name2", ...].
|
||||
// If you want all textures from a tpage, you can just do ["tpage-name"].
|
||||
"textures": [],
|
||||
|
||||
"actors" : [
|
||||
"actors": [
|
||||
{
|
||||
"trans": [-15.2818, 15.2461, 17.1360], // translation
|
||||
"etype": "crate", // actor type
|
||||
"trans": [-15.2818, 15.2461, 17.136], // translation
|
||||
"etype": "crate", // actor type
|
||||
"game_task": "(game-task none)", // associated game task (for powercells, etc)
|
||||
"kill_mask": 0,
|
||||
"quat": [0, 0, 0, 1], // quaternion
|
||||
"bsphere": [-15.2818, 15.2461, 17.1360, 10], // bounding sphere
|
||||
"bsphere": [-15.2818, 15.2461, 17.136, 10], // bounding sphere
|
||||
"lump": {
|
||||
"name": "test-crate",
|
||||
"eco-info": ["eco-info", "(pickup-type gem)", 1]
|
||||
@ -76,15 +78,15 @@
|
||||
},
|
||||
|
||||
{
|
||||
"trans": [-5.4630, 17.4553, 1.6169], // translation
|
||||
"etype": "eco-yellow", // actor type
|
||||
"trans": [-5.463, 17.4553, 1.6169], // translation
|
||||
"etype": "eco-yellow", // actor type
|
||||
"game_task": "(game-task none)", // associated game task (for powercells, etc)
|
||||
"kill_mask": 0,
|
||||
"quat": [0, 0, 0, 1], // quaternion
|
||||
"bsphere": [-5.4630, 17.4553, 1.6169, 10], // bounding sphere
|
||||
"bsphere": [-5.463, 17.4553, 1.6169, 10], // bounding sphere
|
||||
"lump": {
|
||||
"name": "test-eco"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -19628,6 +19628,57 @@
|
||||
(wt31)
|
||||
)
|
||||
|
||||
(defenum water-look
|
||||
(water-anim-sunken-big-room 0)
|
||||
(water-anim-sunken-first-room-from-entrance 1)
|
||||
(water-anim-sunken-qbert-room 2)
|
||||
(water-anim-sunken-first-right-branch 3)
|
||||
(water-anim-sunken-circular-with-bullys 4)
|
||||
(water-anim-sunken-hall-with-one-whirlpool 5)
|
||||
(water-anim-sunken-hall-with-three-whirlpools 6)
|
||||
(water-anim-sunken-start-of-helix-slide 7)
|
||||
(water-anim-sunken-room-above-exit-chamber 8)
|
||||
(water-anim-sunken-hall-before-big-room 9)
|
||||
(water-anim-sunken-dark-eco-qbert 10)
|
||||
(water-anim-sunken-short-piece 11)
|
||||
(water-anim-sunken-big-room-upper-water 12)
|
||||
(water-anim-sunken-dark-eco-platform-room 13)
|
||||
(water-anim-maincave-water-with-crystal 14)
|
||||
(water-anim-maincave-center-pool 15)
|
||||
(water-anim-maincave-lower-right-pool 16)
|
||||
(water-anim-maincave-mid-right-pool 17)
|
||||
(water-anim-maincave-lower-left-pool 18)
|
||||
(water-anim-maincave-mid-left-pool 19)
|
||||
(water-anim-robocave-main-pool 20)
|
||||
(water-anim-misty-mud-by-arena 21)
|
||||
(water-anim-misty-mud-above-skeleton 22)
|
||||
(water-anim-misty-mud-behind-skeleton 23)
|
||||
(water-anim-misty-mud-above-skull-back 24)
|
||||
(water-anim-misty-mud-above-skull-front 25)
|
||||
(water-anim-misty-mud-other-near-skull 26)
|
||||
(water-anim-misty-mud-near-skull 27)
|
||||
(water-anim-misty-mud-under-spine 28)
|
||||
(water-anim-misty-mud-by-dock 29)
|
||||
(water-anim-misty-mud-island-near-dock 30)
|
||||
(water-anim-misty-mud-lonely-island 31)
|
||||
(water-anim-misty-dark-eco-pool 32)
|
||||
(water-anim-ogre-lava 33)
|
||||
(water-anim-jungle-river 34)
|
||||
(water-anim-village3-lava 35)
|
||||
(water-anim-training-lake 36)
|
||||
(water-anim-darkcave-water-with-crystal 37)
|
||||
(water-anim-rolling-water-back 38)
|
||||
(water-anim-rolling-water-front 39)
|
||||
(water-anim-sunken-dark-eco-helix-room 40)
|
||||
(water-anim-finalboss-dark-eco-pool 41)
|
||||
(water-anim-lavatube-energy-lava 42)
|
||||
(water-anim-village1-rice-paddy 43)
|
||||
(water-anim-village1-fountain 44)
|
||||
(water-anim-village1-rice-paddy-mid 45)
|
||||
(water-anim-village1-rice-paddy-top 46)
|
||||
(water-anim-village2-bucket 47)
|
||||
)
|
||||
|
||||
(deftype water-control (basic)
|
||||
((flags water-flags :offset-assert 4)
|
||||
(process process-drawable :offset-assert 8)
|
||||
|
@ -1987,6 +1987,13 @@ void HFragment::read_from_file(TypedRef ref, const decompiler::DecompilerTypeSys
|
||||
size = read_plain_data_field<u32>(ref, "size", dts);
|
||||
}
|
||||
|
||||
void AdgifShaderArray::read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts) {
|
||||
auto length = read_plain_data_field<s32>(ref, "length", dts);
|
||||
adgifs.resize(length);
|
||||
memcpy_plain_data((u8*)adgifs.data(), get_field_ref(ref, "data", dts),
|
||||
sizeof(AdGifData) * length);
|
||||
}
|
||||
|
||||
void BspHeader::read_from_file(const decompiler::LinkedObjectFile& file,
|
||||
const decompiler::DecompilerTypeSystem& dts,
|
||||
GameVersion version,
|
||||
@ -2001,6 +2008,19 @@ void BspHeader::read_from_file(const decompiler::LinkedObjectFile& file,
|
||||
bsphere.read_from_file(get_field_ref(ref, "bsphere", dts));
|
||||
name = read_symbol_field(ref, "name", dts);
|
||||
|
||||
if (version == GameVersion::Jak1) {
|
||||
adgifs.read_from_file(get_and_check_ref_to_basic(ref, "adgifs", "adgif-shader-array", dts),
|
||||
dts);
|
||||
}
|
||||
|
||||
texture_page_count = read_plain_data_field<s32>(ref, "texture-page-count", dts);
|
||||
if (texture_page_count > 0) {
|
||||
auto tex_id_ptr = deref_label(get_field_ref(ref, "texture-ids", dts));
|
||||
for (int i = 0; i < texture_page_count; i++) {
|
||||
texture_ids.push_back(deref_u32(tex_id_ptr, i));
|
||||
}
|
||||
}
|
||||
|
||||
texture_remap_table.clear();
|
||||
s32 tex_remap_len = read_plain_data_field<s32>(ref, "texture-remap-table-len", dts);
|
||||
if (tex_remap_len > 0) {
|
||||
|
@ -840,6 +840,12 @@ struct DrawableTreeArray {
|
||||
std::vector<std::unique_ptr<DrawableTree>> trees;
|
||||
};
|
||||
|
||||
struct AdgifShaderArray {
|
||||
std::vector<AdGifData> adgifs;
|
||||
|
||||
void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts);
|
||||
};
|
||||
|
||||
// The "file info"
|
||||
struct FileInfo {
|
||||
std::string file_type;
|
||||
@ -884,7 +890,9 @@ struct BspHeader {
|
||||
u16 texture_flags[kNumTextureFlags]; // jak 2 only
|
||||
//
|
||||
// (texture-ids (pointer texture-id) :offset-assert 60)
|
||||
std::vector<u32> texture_ids;
|
||||
// (texture-page-count int32 :offset-assert 64)
|
||||
s32 texture_page_count;
|
||||
//
|
||||
// (unk-zero-0 basic :offset-assert 68)
|
||||
//
|
||||
@ -907,6 +915,7 @@ struct BspHeader {
|
||||
// (unk-data-4 float :offset-assert 160)
|
||||
// (unk-data-5 float :offset-assert 164)
|
||||
// (adgifs adgif-shader-array :offset-assert 168)
|
||||
AdgifShaderArray adgifs;
|
||||
// (actor-birth-order (pointer uint32) :offset-assert 172)
|
||||
// (split-box-indices (pointer uint16) :offset-assert 176)
|
||||
// (unk-data-8 uint32 55 :offset-assert 180)
|
||||
|
@ -26,4 +26,7 @@ tfrag3::Texture make_texture(u32 id,
|
||||
bool pool_load);
|
||||
std::vector<level_tools::TextureRemap> extract_tex_remap(const ObjectFileDB& db,
|
||||
const std::string& dgo_name);
|
||||
std::optional<ObjectFileRecord> get_bsp_file(const std::vector<ObjectFileRecord>& records,
|
||||
const std::string& dgo_name);
|
||||
bool is_valid_bsp(const LinkedObjectFile& file);
|
||||
} // namespace decompiler
|
||||
|
@ -353,6 +353,57 @@
|
||||
(anim int32)
|
||||
(ambient-sound-spec sound-spec)))
|
||||
|
||||
;; og:preserve-this added
|
||||
(defenum water-look
|
||||
(water-anim-sunken-big-room 0)
|
||||
(water-anim-sunken-first-room-from-entrance 1)
|
||||
(water-anim-sunken-qbert-room 2)
|
||||
(water-anim-sunken-first-right-branch 3)
|
||||
(water-anim-sunken-circular-with-bullys 4)
|
||||
(water-anim-sunken-hall-with-one-whirlpool 5)
|
||||
(water-anim-sunken-hall-with-three-whirlpools 6)
|
||||
(water-anim-sunken-start-of-helix-slide 7)
|
||||
(water-anim-sunken-room-above-exit-chamber 8)
|
||||
(water-anim-sunken-hall-before-big-room 9)
|
||||
(water-anim-sunken-dark-eco-qbert 10)
|
||||
(water-anim-sunken-short-piece 11)
|
||||
(water-anim-sunken-big-room-upper-water 12)
|
||||
(water-anim-sunken-dark-eco-platform-room 13)
|
||||
(water-anim-maincave-water-with-crystal 14)
|
||||
(water-anim-maincave-center-pool 15)
|
||||
(water-anim-maincave-lower-right-pool 16)
|
||||
(water-anim-maincave-mid-right-pool 17)
|
||||
(water-anim-maincave-lower-left-pool 18)
|
||||
(water-anim-maincave-mid-left-pool 19)
|
||||
(water-anim-robocave-main-pool 20)
|
||||
(water-anim-misty-mud-by-arena 21)
|
||||
(water-anim-misty-mud-above-skeleton 22)
|
||||
(water-anim-misty-mud-behind-skeleton 23)
|
||||
(water-anim-misty-mud-above-skull-back 24)
|
||||
(water-anim-misty-mud-above-skull-front 25)
|
||||
(water-anim-misty-mud-other-near-skull 26)
|
||||
(water-anim-misty-mud-near-skull 27)
|
||||
(water-anim-misty-mud-under-spine 28)
|
||||
(water-anim-misty-mud-by-dock 29)
|
||||
(water-anim-misty-mud-island-near-dock 30)
|
||||
(water-anim-misty-mud-lonely-island 31)
|
||||
(water-anim-misty-dark-eco-pool 32)
|
||||
(water-anim-ogre-lava 33)
|
||||
(water-anim-jungle-river 34)
|
||||
(water-anim-village3-lava 35)
|
||||
(water-anim-training-lake 36)
|
||||
(water-anim-darkcave-water-with-crystal 37)
|
||||
(water-anim-rolling-water-back 38)
|
||||
(water-anim-rolling-water-front 39)
|
||||
(water-anim-sunken-dark-eco-helix-room 40)
|
||||
(water-anim-finalboss-dark-eco-pool 41)
|
||||
(water-anim-lavatube-energy-lava 42)
|
||||
(water-anim-village1-rice-paddy 43)
|
||||
(water-anim-village1-fountain 44)
|
||||
(water-anim-village1-rice-paddy-mid 45)
|
||||
(water-anim-village1-rice-paddy-top 46)
|
||||
(water-anim-village2-bucket 47))
|
||||
|
||||
(define *water-anim-look*
|
||||
(new 'static
|
||||
'boxed-array
|
||||
|
@ -2485,10 +2485,11 @@
|
||||
:music-bank #f
|
||||
:ambient-sounds
|
||||
'()
|
||||
:mood '*default-mood*
|
||||
:mood-func 'update-mood-default
|
||||
:ocean #f
|
||||
:mood '*village1-mood*
|
||||
:mood-func 'update-mood-village1
|
||||
:ocean '*ocean-map-village1*
|
||||
:sky #t
|
||||
:sun-fade 1.0
|
||||
:continues
|
||||
'((new 'static
|
||||
'continue-point
|
||||
@ -2507,8 +2508,8 @@
|
||||
:vis-nick 'none
|
||||
:lev0 'test-zone
|
||||
:disp0 'display
|
||||
:lev1 'village1
|
||||
:disp1 'display))
|
||||
:lev1 #f
|
||||
:disp1 #f))
|
||||
:tasks
|
||||
'()
|
||||
:priority 100
|
||||
|
@ -74,6 +74,44 @@ size_t generate_u32_array(const std::vector<u32>& array, DataObjectGenerator& ge
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t generate_adgif_shader_array(const std::vector<u32>& data, DataObjectGenerator& gen) {
|
||||
gen.align_to_basic();
|
||||
gen.add_type_tag("adgif-shader-array");
|
||||
size_t result = gen.current_offset_bytes();
|
||||
for (auto& word : data) {
|
||||
gen.add_word(word);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t generate_adgif_shader_array(const AdgifShaderArray& adgifs, DataObjectGenerator& gen) {
|
||||
gen.align_to_basic();
|
||||
gen.add_type_tag("adgif-shader-array");
|
||||
size_t result = gen.current_offset_bytes();
|
||||
gen.add_word(adgifs.adgifs.size());
|
||||
gen.add_word(adgifs.adgifs.size());
|
||||
gen.add_word(0);
|
||||
for (auto& adgif : adgifs.adgifs) {
|
||||
for (size_t i = 0; i < sizeof(AdGifData) / sizeof(u32); i++) {
|
||||
u32 data;
|
||||
memcpy(&data, (u32*)&adgif + i, sizeof(u32));
|
||||
gen.add_word(data);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t generate_tex_remap_table(const std::vector<TexRemap>& remap_table,
|
||||
DataObjectGenerator& gen) {
|
||||
gen.align(4);
|
||||
size_t result = gen.current_offset_bytes();
|
||||
for (auto& entry : remap_table) {
|
||||
gen.add_word(entry.orig_texid);
|
||||
gen.add_word(entry.new_texid);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<u8> LevelFile::save_object_file() const {
|
||||
DataObjectGenerator gen;
|
||||
gen.add_type_tag("bsp-header");
|
||||
@ -97,9 +135,15 @@ std::vector<u8> LevelFile::save_object_file() const {
|
||||
//(pat pointer :offset-assert 44)
|
||||
//(pat-length int32 :offset-assert 48)
|
||||
//(texture-remap-table (pointer uint64) :offset-assert 52)
|
||||
if (!texture_remap_table.empty())
|
||||
gen.link_word_to_byte(52 / 4, generate_tex_remap_table(texture_remap_table, gen));
|
||||
//(texture-remap-table-len int32 :offset-assert 56)
|
||||
gen.set_word(56 / 4, texture_remap_table.size());
|
||||
//(texture-ids (pointer texture-id) :offset-assert 60)
|
||||
if (!texture_ids.empty())
|
||||
gen.link_word_to_byte(60 / 4, generate_u32_array(texture_ids, gen));
|
||||
//(texture-page-count int32 :offset-assert 64)
|
||||
gen.set_word(64 / 4, texture_ids.size());
|
||||
//(unk-zero-0 basic :offset-assert 68)
|
||||
//(name symbol :offset-assert 72)
|
||||
gen.link_word_to_symbol(name, 72 / 4);
|
||||
@ -120,6 +164,8 @@ std::vector<u8> LevelFile::save_object_file() const {
|
||||
//(unk-data-4 float :offset-assert 160)
|
||||
//(unk-data-5 float :offset-assert 164)
|
||||
//(adgifs adgif-shader-array :offset-assert 168)
|
||||
if (!adgifs.adgifs.empty())
|
||||
gen.link_word_to_byte(168 / 4, generate_adgif_shader_array(adgifs, gen));
|
||||
//(actor-birth-order (pointer uint32) :offset-assert 172)
|
||||
gen.link_word_to_byte(172 / 4, generate_u32_array(actor_birth_order, gen));
|
||||
//(split-box-indices (pointer uint16) :offset-assert 176)
|
||||
|
@ -36,7 +36,10 @@ struct DrawableTreeArray {
|
||||
size_t add_to_object_file(DataObjectGenerator& gen) const;
|
||||
};
|
||||
|
||||
struct TextureRemap {};
|
||||
struct TexRemap {
|
||||
u32 orig_texid;
|
||||
u32 new_texid;
|
||||
};
|
||||
|
||||
struct TextureId {};
|
||||
|
||||
@ -52,7 +55,9 @@ struct DrawableInlineArrayAmbient {
|
||||
std::vector<EntityAmbient> ambients;
|
||||
};
|
||||
|
||||
struct AdgifShaderArray {};
|
||||
struct AdgifShaderArray {
|
||||
std::vector<AdGifData> adgifs;
|
||||
};
|
||||
|
||||
// This is a place to collect all the data that should go into the bsp-header file.
|
||||
struct LevelFile {
|
||||
@ -72,11 +77,11 @@ struct LevelFile {
|
||||
|
||||
// (texture-remap-table (pointer uint64) :offset-assert 52)
|
||||
// (texture-remap-table-len int32 :offset-assert 56)
|
||||
std::vector<TextureRemap> texture_remap_table;
|
||||
std::vector<TexRemap> texture_remap_table;
|
||||
|
||||
// (texture-ids (pointer texture-id) :offset-assert 60)
|
||||
// (texture-page-count int32 :offset-assert 64)
|
||||
std::vector<TextureId> texture_ids;
|
||||
std::vector<u32> texture_ids;
|
||||
|
||||
// (unk-zero-0 basic :offset-assert 68)
|
||||
// "misc", seems like it can be zero and is unused.
|
||||
|
@ -3,6 +3,8 @@
|
||||
#include "common/util/gltf_util.h"
|
||||
|
||||
#include "decompiler/extractor/extractor_util.h"
|
||||
#include "decompiler/level_extractor/BspHeader.h"
|
||||
#include "decompiler/level_extractor/extract_level.h"
|
||||
#include "decompiler/level_extractor/extract_merc.h"
|
||||
#include "goalc/build_level/collide/jak1/collide_bvh.h"
|
||||
#include "goalc/build_level/collide/jak1/collide_pack.h"
|
||||
@ -17,7 +19,7 @@ bool run_build_level(const std::string& input_file,
|
||||
const std::string& output_prefix) {
|
||||
auto level_json = parse_commented_json(
|
||||
file_util::read_text_file(file_util::get_file_path({input_file})), input_file);
|
||||
LevelFile file; // GOAL level file
|
||||
LevelFile file{}; // GOAL level file
|
||||
tfrag3::Level pc_level; // PC level file
|
||||
gltf_util::TexturePool tex_pool; // pc level texture pool
|
||||
|
||||
@ -48,7 +50,7 @@ bool run_build_level(const std::string& input_file,
|
||||
// actors
|
||||
std::vector<EntityActor> actors;
|
||||
auto dts = decompiler::DecompilerTypeSystem(GameVersion::Jak1);
|
||||
dts.parse_enum_defs({"decompiler", "config", "jak1", "all-types.gc"});
|
||||
dts.parse_type_defs({"decompiler", "config", "jak1", "all-types.gc"});
|
||||
add_actors_from_json(level_json.at("actors"), actors, level_json.value("base_id", 1234), dts);
|
||||
std::sort(actors.begin(), actors.end(), [](auto& a, auto& b) { return a.aid < b.aid; });
|
||||
auto duplicates = std::adjacent_find(actors.begin(), actors.end(),
|
||||
@ -104,13 +106,9 @@ bool run_build_level(const std::string& input_file,
|
||||
collide_drawable_tree.packed_frags = pack_collide_frags(collide_drawable_tree.bvh.frags.frags);
|
||||
}
|
||||
|
||||
// Save the GOAL level
|
||||
auto result = file.save_object_file();
|
||||
lg::print("Level bsp file size {} bytes\n", result.size());
|
||||
auto save_path = file_util::get_jak_project_dir() / bsp_output_file;
|
||||
file_util::create_dir_if_needed_for_file(save_path);
|
||||
lg::print("Saving to {}\n", save_path.string());
|
||||
file_util::write_binary_file(save_path, result.data(), result.size());
|
||||
auto sky_name = level_json.value("sky", "none");
|
||||
auto texture_remap = level_json.value("tex_remap", "none");
|
||||
auto tpages = level_json.value("tpages", std::vector<u32>({}));
|
||||
|
||||
// Add textures and models
|
||||
// TODO remove hardcoded config settings
|
||||
@ -161,20 +159,64 @@ bool run_build_level(const std::string& input_file,
|
||||
|
||||
std::vector<std::string> processed_art_groups;
|
||||
|
||||
// find all art groups used by the custom level in other dgos
|
||||
if (level_json.contains("art_groups") && !level_json.at("art_groups").empty()) {
|
||||
// find all art groups used by the custom level in other dgos and extract sky and texture remap
|
||||
// if desired
|
||||
auto should_process_art_groups =
|
||||
(level_json.contains("art_groups") && !level_json.at("art_groups").empty()) ||
|
||||
(sky_name != "none" || texture_remap != "none");
|
||||
if (should_process_art_groups) {
|
||||
for (auto& dgo : config.dgo_names) {
|
||||
// remove "DGO/" prefix
|
||||
const auto& dgo_name = dgo.substr(4);
|
||||
const auto& files = db.obj_files_by_dgo.at(dgo_name);
|
||||
auto art_groups =
|
||||
find_art_groups(processed_art_groups,
|
||||
level_json.at("art_groups").get<std::vector<std::string>>(), files);
|
||||
auto tex_remap = decompiler::extract_tex_remap(db, dgo_name);
|
||||
level_json.value("art_groups", std::vector<std::string>{}), files);
|
||||
std::vector<level_tools::TextureRemap> tex_remap{};
|
||||
if (auto bsp = get_bsp_file(files, dgo_name)) {
|
||||
const auto& link_data = db.lookup_record(bsp.value()).linked_data;
|
||||
if (is_valid_bsp(link_data)) {
|
||||
level_tools::BspHeader level_file;
|
||||
level_file.read_from_file(link_data, dts, GameVersion::Jak1, true);
|
||||
auto bsp_name = bsp.value().name.substr(0, bsp.value().name.size() - 4);
|
||||
tex_remap = level_file.texture_remap_table;
|
||||
auto is_sky_bsp = bsp_name == sky_name;
|
||||
auto is_tex_remap_bsp = bsp_name == texture_remap;
|
||||
auto sky_and_tex_remap_same = sky_name == texture_remap;
|
||||
if (is_tex_remap_bsp) {
|
||||
lg::info("custom level: copying texture remap data from {}", texture_remap);
|
||||
// copy texture remap data from bsp
|
||||
file.texture_remap_table.resize(tex_remap.size());
|
||||
memcpy(file.texture_remap_table.data(), level_file.texture_remap_table.data(),
|
||||
tex_remap.size() * sizeof(level_tools::TextureRemap));
|
||||
}
|
||||
if (is_sky_bsp) {
|
||||
// copy adgif data from bsp
|
||||
lg::info("custom level: copying adgifs from {}", sky_name);
|
||||
auto& adgifs = file.adgifs.adgifs;
|
||||
adgifs.resize(level_file.adgifs.adgifs.size());
|
||||
memcpy(adgifs.data(), level_file.adgifs.adgifs.data(),
|
||||
level_file.adgifs.adgifs.size() * sizeof(AdGifData));
|
||||
}
|
||||
if (sky_and_tex_remap_same && is_sky_bsp && is_tex_remap_bsp && tpages.empty()) {
|
||||
// if tpages json is empty and sky and tex remap are the same level, auto fill
|
||||
file.texture_ids.resize(level_file.texture_page_count);
|
||||
memcpy(file.texture_ids.data(), level_file.texture_ids.data(),
|
||||
sizeof(u32) * level_file.texture_page_count);
|
||||
std::vector<u32> tex_ids;
|
||||
tex_ids.reserve(level_file.texture_page_count);
|
||||
for (auto& id : level_file.texture_ids) {
|
||||
tex_ids.push_back(id >> 20);
|
||||
}
|
||||
lg::info("custom level: login tpages automatically set to [{}]",
|
||||
fmt::join(tex_ids, ", "));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto& ag : art_groups) {
|
||||
if (ag.name.length() > 3 && !ag.name.compare(ag.name.length() - 3, 3, "-ag")) {
|
||||
const auto& ag_file = db.lookup_record(ag);
|
||||
lg::print("custom level: extracting art group {}\n", ag_file.name_in_dgo);
|
||||
lg::info("custom level: extracting art group {}", ag_file.name_in_dgo);
|
||||
decompiler::extract_merc(ag_file, tex_db, db.dts, tex_remap, pc_level, false,
|
||||
db.version());
|
||||
}
|
||||
@ -184,31 +226,69 @@ bool run_build_level(const std::string& input_file,
|
||||
|
||||
// add textures
|
||||
if (level_json.contains("textures") && !level_json.at("textures").empty()) {
|
||||
std::vector<std::string> processed_textures;
|
||||
std::vector<std::string> wanted_texs =
|
||||
level_json.at("textures").get<std::vector<std::string>>();
|
||||
std::map<std::string, std::vector<std::string>> processed_textures;
|
||||
auto tex_json = level_json.value("textures", std::vector<std::vector<std::string>>{});
|
||||
std::map<std::string, std::vector<std::string>> wanted_texs;
|
||||
for (auto& arr : tex_json) {
|
||||
auto tpage_name = arr[0];
|
||||
// we only want a select few textures
|
||||
if (arr.size() > 1) {
|
||||
for (size_t i = 1; i < arr.size(); i++) {
|
||||
wanted_texs.insert({tpage_name, {arr.begin() + 1, arr.end()}});
|
||||
}
|
||||
} else {
|
||||
// we want all textures from this tpage
|
||||
auto it = std::find_if(tex_db.tpage_names.begin(), tex_db.tpage_names.end(),
|
||||
[tpage_name](const std::pair<u32, std::string>& t) {
|
||||
return t.second == tpage_name;
|
||||
});
|
||||
if (it != tex_db.tpage_names.end()) {
|
||||
lg::info("custom level: adding all textures from tpage {}:", tpage_name);
|
||||
std::vector<std::string> tex_names;
|
||||
for (auto& [id, tex] : tex_db.textures) {
|
||||
if (tex_db.tpage_names.at(tex.page) == tpage_name) {
|
||||
lg::info("custom level: adding texture {} (tpage {})", tex.name, tex.page);
|
||||
tex_names.push_back(tex.name);
|
||||
pc_level.textures.push_back(make_texture(id, tex, tpage_name, true));
|
||||
processed_textures[tpage_name].push_back(tex.name);
|
||||
}
|
||||
}
|
||||
wanted_texs.insert({tpage_name, tex_names});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// first check the texture is not already in the level
|
||||
for (auto& level_tex : pc_level.textures) {
|
||||
if (std::find(wanted_texs.begin(), wanted_texs.end(), level_tex.debug_name) !=
|
||||
wanted_texs.end()) {
|
||||
processed_textures.push_back(level_tex.debug_name);
|
||||
auto tpage = level_tex.debug_tpage_name;
|
||||
auto name = level_tex.debug_name;
|
||||
auto it = std::find_if(
|
||||
wanted_texs.begin(), wanted_texs.end(),
|
||||
[tpage, name](const std::pair<std::string, std::vector<std::string>>& elt) {
|
||||
return elt.first == tpage &&
|
||||
std::find(elt.second.begin(), elt.second.end(), name) != elt.second.end();
|
||||
});
|
||||
if (it != wanted_texs.end()) {
|
||||
processed_textures[level_tex.debug_tpage_name].push_back(level_tex.debug_name);
|
||||
}
|
||||
}
|
||||
|
||||
// then add
|
||||
for (auto& [id, tex] : tex_db.textures) {
|
||||
for (auto& tex0 : wanted_texs) {
|
||||
if (std::find(processed_textures.begin(), processed_textures.end(), tex.name) !=
|
||||
processed_textures.end()) {
|
||||
auto db_tpage_name = tex_db.tpage_names.at(tex.page);
|
||||
for (auto& [wanted_tpage_name, wanted_tex_list] : wanted_texs) {
|
||||
auto processed = processed_textures[db_tpage_name];
|
||||
if (std::find(processed.begin(), processed.end(), tex.name) != processed.end()) {
|
||||
// lg::info("custom level: ignoring duplicate texture {} from {}", tex.name, tex.page);
|
||||
continue;
|
||||
}
|
||||
if (tex.name == tex0) {
|
||||
lg::info("custom level: adding texture {} from tpage {} ({})", tex.name, tex.page,
|
||||
tex_db.tpage_names.at(tex.page));
|
||||
pc_level.textures.push_back(
|
||||
make_texture(id, tex, tex_db.tpage_names.at(tex.page), true));
|
||||
processed_textures.push_back(tex.name);
|
||||
}
|
||||
for (auto& wanted_tex : wanted_tex_list)
|
||||
if (db_tpage_name == wanted_tpage_name && tex.name == wanted_tex) {
|
||||
lg::info("custom level: adding texture {} from tpage {} ({})", tex.name, tex.page,
|
||||
db_tpage_name);
|
||||
pc_level.textures.push_back(make_texture(id, tex, db_tpage_name, true));
|
||||
processed_textures[db_tpage_name].push_back(tex.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -222,6 +302,14 @@ bool run_build_level(const std::string& input_file,
|
||||
}
|
||||
}
|
||||
|
||||
// Save the GOAL level
|
||||
auto result = file.save_object_file();
|
||||
lg::print("Level bsp file size {} bytes\n", result.size());
|
||||
auto save_path = file_util::get_jak_project_dir() / bsp_output_file;
|
||||
file_util::create_dir_if_needed_for_file(save_path);
|
||||
lg::print("Saving to {}\n", save_path.string());
|
||||
file_util::write_binary_file(save_path, result.data(), result.size());
|
||||
|
||||
// Save the PC level
|
||||
save_pc_data(file.name, pc_level,
|
||||
file_util::get_jak_project_dir() / "out" / output_prefix / "fr3");
|
||||
|
Loading…
Reference in New Issue
Block a user