mirror of
https://github.com/open-goal/jak-project
synced 2026-07-11 23:30:16 -04:00
refactor level building code, split into jak 1 and jak 2 versions
This commit is contained in:
+19
-12
@@ -5,18 +5,25 @@ add_library(compiler
|
||||
emitter/ObjectGenerator.cpp
|
||||
emitter/Register.cpp
|
||||
debugger/disassemble.cpp
|
||||
build_level/build_level.cpp
|
||||
build_level/collide_bvh.cpp
|
||||
build_level/collide_drawable.cpp
|
||||
build_level/collide_pack.cpp
|
||||
build_level/color_quantization.cpp
|
||||
build_level/Entity.cpp
|
||||
build_level/FileInfo.cpp
|
||||
build_level/gltf_mesh_extract.cpp
|
||||
build_level/LevelFile.cpp
|
||||
build_level/ResLump.cpp
|
||||
build_level/Tfrag.cpp
|
||||
build_level/ambient.cpp
|
||||
build_level/common/build_level.cpp
|
||||
build_level/jak1/build_level.cpp
|
||||
build_level/jak2/build_level.cpp
|
||||
build_level/collide/jak1/collide_bvh.cpp
|
||||
build_level/collide/jak1/collide_drawable.cpp
|
||||
build_level/collide/jak1/collide_pack.cpp
|
||||
build_level/common/color_quantization.cpp
|
||||
build_level/common/Entity.cpp
|
||||
build_level/jak1/Entity.cpp
|
||||
build_level/jak2/Entity.cpp
|
||||
build_level/common/FileInfo.cpp
|
||||
build_level/jak1/FileInfo.cpp
|
||||
build_level/jak2/FileInfo.cpp
|
||||
build_level/common/gltf_mesh_extract.cpp
|
||||
build_level/jak1/LevelFile.cpp
|
||||
build_level/jak2/LevelFile.cpp
|
||||
build_level/common/ResLump.cpp
|
||||
build_level/common/Tfrag.cpp
|
||||
build_level/jak1/ambient.cpp
|
||||
compiler/Compiler.cpp
|
||||
compiler/Env.cpp
|
||||
compiler/Val.cpp
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
#pragma once
|
||||
#include "common/common_types.h"
|
||||
#include "common/math/Vector.h"
|
||||
|
||||
struct CollideVertex {
|
||||
float x, y, z;
|
||||
};
|
||||
|
||||
namespace jak1 {
|
||||
struct PatSurface {
|
||||
enum class Mode { GROUND = 0, WALL = 1, OBSTACLE = 2, MAX_MODE = 3 };
|
||||
enum class Material {
|
||||
STONE = 0,
|
||||
ICE = 1,
|
||||
QUICKSAND = 2,
|
||||
WATERBOTTOM = 3,
|
||||
TAR = 4,
|
||||
SAND = 5,
|
||||
WOOD = 6,
|
||||
GRASS = 7,
|
||||
PCMETAL = 8,
|
||||
SNOW = 9,
|
||||
DEEPSNOW = 10,
|
||||
HOTCOALS = 11,
|
||||
LAVA = 12,
|
||||
CRWOOD = 13,
|
||||
GRAVEL = 14,
|
||||
DIRT = 15,
|
||||
METAL = 16,
|
||||
STRAW = 17,
|
||||
TUBE = 18,
|
||||
SWAMP = 19,
|
||||
STOPPROJ = 20,
|
||||
ROTATE = 21,
|
||||
NEUTRAL = 22,
|
||||
MAX_MATERIAL = 23
|
||||
};
|
||||
|
||||
enum class Event {
|
||||
NONE = 0,
|
||||
DEADLY = 1,
|
||||
ENDLESSFALL = 2,
|
||||
BURN = 3,
|
||||
DEADLYUP = 4,
|
||||
BURNUP = 5,
|
||||
MELT = 6,
|
||||
MAX_EVENT = 7,
|
||||
};
|
||||
|
||||
void set_noentity(bool x) {
|
||||
if (x) {
|
||||
val |= (1 << 0);
|
||||
} else {
|
||||
val &= ~(1 << 0);
|
||||
}
|
||||
}
|
||||
bool get_noentity() const { return val & (1 << 0); }
|
||||
|
||||
void set_nocamera(bool x) {
|
||||
if (x) {
|
||||
val |= (1 << 1);
|
||||
} else {
|
||||
val &= ~(1 << 1);
|
||||
}
|
||||
}
|
||||
bool get_nocamera() const { return val & (1 << 1); }
|
||||
|
||||
void set_noedge(bool x) {
|
||||
if (x) {
|
||||
val |= (1 << 2);
|
||||
} else {
|
||||
val &= ~(1 << 2);
|
||||
}
|
||||
}
|
||||
bool get_noedge() const { return val & (1 << 2); }
|
||||
|
||||
void set_mode(Mode mode) {
|
||||
val &= ~(0b111 << 3);
|
||||
val |= ((u32)mode << 3);
|
||||
}
|
||||
Mode get_mode() const { return (Mode)(0b111 & (val >> 3)); }
|
||||
|
||||
void set_material(Material mat) {
|
||||
val &= ~(0b111111 << 6);
|
||||
val |= ((u32)mat << 6);
|
||||
}
|
||||
Material get_material() const { return (Material)(0b111111 & (val >> 6)); }
|
||||
|
||||
void set_nolineofsight(bool x) {
|
||||
if (x) {
|
||||
val |= (1 << 12);
|
||||
} else {
|
||||
val &= ~(1 << 12);
|
||||
}
|
||||
}
|
||||
bool get_nolineofsight() const { return val & (1 << 12); }
|
||||
|
||||
void set_event(Event ev) {
|
||||
val &= ~(0b111111 << 14);
|
||||
val |= ((u32)ev << 14);
|
||||
}
|
||||
Event get_event() const { return (Event)(0b111111 & (val >> 14)); }
|
||||
|
||||
bool operator==(const PatSurface& other) const { return val == other.val; }
|
||||
// bits 13, [15-31] are unused, or have unknown purpose.
|
||||
u32 val = 0;
|
||||
};
|
||||
|
||||
struct CollideFace {
|
||||
math::Vector4f bsphere;
|
||||
math::Vector3f v[3];
|
||||
PatSurface pat;
|
||||
};
|
||||
} // namespace jak1
|
||||
|
||||
namespace jak2 {
|
||||
struct PatSurface {
|
||||
enum class Mode { GROUND = 0, WALL = 1, OBSTACLE = 2, HALFPIPE = 3, MAX_MODE = 4 };
|
||||
enum class Material {
|
||||
NONE = 0,
|
||||
ICE = 1,
|
||||
QUICKSAND = 2,
|
||||
WATERBOTTOM = 3,
|
||||
TAR = 4,
|
||||
SAND = 5,
|
||||
WOOD = 6,
|
||||
GRASS = 7,
|
||||
PCMETAL = 8,
|
||||
SNOW = 9,
|
||||
DEEPSNOW = 10,
|
||||
HOTCOALS = 11,
|
||||
LAVA = 12,
|
||||
CRWOOD = 13,
|
||||
GRAVEL = 14,
|
||||
DIRT = 15,
|
||||
METAL = 16,
|
||||
STRAW = 17,
|
||||
TUBE = 18,
|
||||
SWAMP = 19,
|
||||
STOPPROJ = 20,
|
||||
ROTATE = 21,
|
||||
NEUTRAL = 22,
|
||||
STONE = 23,
|
||||
CRMETAL = 24,
|
||||
CARPET = 25,
|
||||
GRMETAL = 26,
|
||||
SHMETAL = 27,
|
||||
HDWOOD = 28,
|
||||
MAX_MATERIAL = 29
|
||||
};
|
||||
|
||||
enum class Event {
|
||||
NONE = 0,
|
||||
DEADLY = 1,
|
||||
ENDLESSFALL = 2,
|
||||
BURN = 3,
|
||||
DEADLYUP = 4,
|
||||
BURNUP = 5,
|
||||
MELT = 6,
|
||||
SLIDE = 7,
|
||||
LIP = 8,
|
||||
LIPRAMP = 9,
|
||||
SHOCK = 10,
|
||||
SHOCKUP = 11,
|
||||
HIDE = 12,
|
||||
RAIL = 13,
|
||||
SLIPPERY = 14,
|
||||
MAX_EVENT = 15,
|
||||
};
|
||||
|
||||
void set_noentity(bool x) {
|
||||
if (x) {
|
||||
val |= (1 << 0);
|
||||
} else {
|
||||
val &= ~(1 << 0);
|
||||
}
|
||||
}
|
||||
bool get_noentity() const { return val & (1 << 0); }
|
||||
|
||||
void set_nocamera(bool x) {
|
||||
if (x) {
|
||||
val |= (1 << 1);
|
||||
} else {
|
||||
val &= ~(1 << 1);
|
||||
}
|
||||
}
|
||||
bool get_nocamera() const { return val & (1 << 1); }
|
||||
|
||||
void set_noedge(bool x) {
|
||||
if (x) {
|
||||
val |= (1 << 2);
|
||||
} else {
|
||||
val &= ~(1 << 2);
|
||||
}
|
||||
}
|
||||
bool get_noedge() const { return val & (1 << 2); }
|
||||
|
||||
void set_nogrind(bool x) {
|
||||
if (x) {
|
||||
val |= (1 << 3);
|
||||
} else {
|
||||
val &= ~(1 << 3);
|
||||
}
|
||||
}
|
||||
bool get_nogrind() const { return val & (1 << 3); }
|
||||
|
||||
void set_nojak(bool x) {
|
||||
if (x) {
|
||||
val |= (1 << 4);
|
||||
} else {
|
||||
val &= ~(1 << 4);
|
||||
}
|
||||
}
|
||||
bool get_nojak() const { return val & (1 << 4); }
|
||||
|
||||
void set_noboard(bool x) {
|
||||
if (x) {
|
||||
val |= (1 << 5);
|
||||
} else {
|
||||
val &= ~(1 << 5);
|
||||
}
|
||||
}
|
||||
bool get_noboard() const { return val & (1 << 5); }
|
||||
|
||||
void set_nopilot(bool x) {
|
||||
if (x) {
|
||||
val |= (1 << 6);
|
||||
} else {
|
||||
val &= ~(1 << 6);
|
||||
}
|
||||
}
|
||||
bool get_nopilot() const { return val & (1 << 6); }
|
||||
|
||||
void set_mode(Mode mode) {
|
||||
val &= ~(0b111 << 7);
|
||||
val |= ((u32)mode << 7);
|
||||
}
|
||||
Mode get_mode() const { return (Mode)(0b111 & (val >> 7)); }
|
||||
|
||||
void set_material(Material mat) {
|
||||
val &= ~(0b111111 << 10);
|
||||
val |= ((u32)mat << 10);
|
||||
}
|
||||
Material get_material() const { return (Material)(0b111111 & (val >> 10)); }
|
||||
|
||||
void set_nolineofsight(bool x) {
|
||||
if (x) {
|
||||
val |= (1 << 16);
|
||||
} else {
|
||||
val &= ~(1 << 16);
|
||||
}
|
||||
}
|
||||
bool get_nolineofsight() const { return val & (1 << 16); }
|
||||
|
||||
void set_event(Event ev) {
|
||||
val &= ~(0b111111 << 18);
|
||||
val |= ((u32)ev << 18);
|
||||
}
|
||||
Event get_event() const { return (Event)(0b111111 & (val >> 18)); }
|
||||
|
||||
void set_probe(bool x) {
|
||||
if (x) {
|
||||
val |= (1 << 24);
|
||||
} else {
|
||||
val &= ~(1 << 24);
|
||||
}
|
||||
}
|
||||
bool get_probe() const { return val & (1 << 24); }
|
||||
|
||||
void set_nomech(bool x) {
|
||||
if (x) {
|
||||
val |= (1 << 25);
|
||||
} else {
|
||||
val &= ~(1 << 25);
|
||||
}
|
||||
}
|
||||
bool get_nomech() const { return val & (1 << 25); }
|
||||
|
||||
void set_noproj(bool x) {
|
||||
if (x) {
|
||||
val |= (1 << 26);
|
||||
} else {
|
||||
val &= ~(1 << 26);
|
||||
}
|
||||
}
|
||||
bool get_noproj() const { return val & (1 << 26); }
|
||||
|
||||
void set_noendlessfall(bool x) {
|
||||
if (x) {
|
||||
val |= (1 << 27);
|
||||
} else {
|
||||
val &= ~(1 << 27);
|
||||
}
|
||||
}
|
||||
bool get_noendlessfall() const { return val & (1 << 27); }
|
||||
|
||||
void set_noprobe(bool x) {
|
||||
if (x) {
|
||||
val |= (1 << 28);
|
||||
} else {
|
||||
val &= ~(1 << 28);
|
||||
}
|
||||
}
|
||||
bool get_noprobe() const { return val & (1 << 28); }
|
||||
|
||||
bool operator==(const PatSurface& other) const { return val == other.val; }
|
||||
u32 val = 0;
|
||||
};
|
||||
|
||||
struct CollideFace {
|
||||
math::Vector4f bsphere;
|
||||
math::Vector3f v[3];
|
||||
PatSurface pat;
|
||||
};
|
||||
} // namespace jak2
|
||||
@@ -27,7 +27,7 @@ constexpr int MAX_UNIQUE_VERTS_IN_FRAG = 128;
|
||||
*/
|
||||
struct CNode {
|
||||
std::vector<CNode> child_nodes;
|
||||
std::vector<CollideFace> faces;
|
||||
std::vector<jak1::CollideFace> faces;
|
||||
math::Vector4f bsphere;
|
||||
};
|
||||
|
||||
@@ -101,13 +101,14 @@ void compute_my_bsphere_ritters(CNode& node) {
|
||||
* Split faces in two along a coordinate plane.
|
||||
* Will clear the input faces
|
||||
*/
|
||||
void split_along_dim(std::vector<CollideFace>& faces,
|
||||
void split_along_dim(std::vector<jak1::CollideFace>& faces,
|
||||
int dim,
|
||||
std::vector<CollideFace>* out0,
|
||||
std::vector<CollideFace>* out1) {
|
||||
std::sort(faces.begin(), faces.end(), [=](const CollideFace& a, const CollideFace& b) {
|
||||
return a.bsphere[dim] < b.bsphere[dim];
|
||||
});
|
||||
std::vector<jak1::CollideFace>* out0,
|
||||
std::vector<jak1::CollideFace>* out1) {
|
||||
std::sort(faces.begin(), faces.end(),
|
||||
[=](const jak1::CollideFace& a, const jak1::CollideFace& b) {
|
||||
return a.bsphere[dim] < b.bsphere[dim];
|
||||
});
|
||||
lg::print("splitting with size: {}\n", faces.size());
|
||||
size_t split_idx = faces.size() / 2;
|
||||
out0->insert(out0->end(), faces.begin(), faces.begin() + split_idx);
|
||||
@@ -278,7 +279,7 @@ void debug_stats(const CollideTree& tree) {
|
||||
|
||||
} // namespace
|
||||
|
||||
CollideTree construct_collide_bvh(const std::vector<CollideFace>& tris) {
|
||||
CollideTree construct_collide_bvh(const std::vector<jak1::CollideFace>& tris) {
|
||||
// part 1: build the tree
|
||||
Timer bvh_timer;
|
||||
lg::info("Building collide bvh from {} triangles", tris.size());
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "goalc/build_level/collide_common.h"
|
||||
#include "goalc/build_level/collide/common/collide_common.h"
|
||||
|
||||
// requirements:
|
||||
// max depth of 3 (maybe?)
|
||||
@@ -19,7 +19,7 @@ struct DrawNode {
|
||||
|
||||
struct CollideFrag {
|
||||
math::Vector4f bsphere;
|
||||
std::vector<CollideFace> faces;
|
||||
std::vector<jak1::CollideFace> faces;
|
||||
};
|
||||
|
||||
struct DrawableInlineArrayNode {
|
||||
@@ -36,5 +36,5 @@ struct CollideTree {
|
||||
DrawableInlineArrayCollideFrag frags;
|
||||
};
|
||||
|
||||
CollideTree construct_collide_bvh(const std::vector<CollideFace>& tris);
|
||||
CollideTree construct_collide_bvh(const std::vector<jak1::CollideFace>& tris);
|
||||
} // namespace collide
|
||||
+1
-1
@@ -68,7 +68,7 @@
|
||||
:size-assert #x44
|
||||
*/
|
||||
|
||||
size_t generate_pat_array(DataObjectGenerator& gen, const std::vector<PatSurface>& pats) {
|
||||
size_t generate_pat_array(DataObjectGenerator& gen, const std::vector<jak1::PatSurface>& pats) {
|
||||
gen.align_to_basic();
|
||||
size_t result = gen.current_offset_bytes();
|
||||
for (auto& pat : pats) {
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "goalc/build_level/collide_bvh.h"
|
||||
#include "goalc/build_level/collide_pack.h"
|
||||
#include "collide_bvh.h"
|
||||
#include "collide_pack.h"
|
||||
|
||||
class DataObjectGenerator;
|
||||
|
||||
+4
-4
@@ -91,7 +91,7 @@ PackedU16Verts pack_verts_to_u16(const std::vector<math::Vector3f>& input) {
|
||||
}
|
||||
|
||||
struct PatSurfaceHash {
|
||||
size_t operator()(const PatSurface& in) const { return std::hash<u32>()(in.val); }
|
||||
size_t operator()(const jak1::PatSurface& in) const { return std::hash<u32>()(in.val); }
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -99,10 +99,10 @@ struct PatSurfaceHash {
|
||||
* There's a pat "palette" with up 255 unique pats.
|
||||
*/
|
||||
struct PatMap {
|
||||
std::unordered_map<PatSurface, u32, PatSurfaceHash> map;
|
||||
std::vector<PatSurface> pats;
|
||||
std::unordered_map<jak1::PatSurface, u32, PatSurfaceHash> map;
|
||||
std::vector<jak1::PatSurface> pats;
|
||||
|
||||
u32 add_pat(PatSurface pat) {
|
||||
u32 add_pat(jak1::PatSurface pat) {
|
||||
const auto& lookup = map.find(pat);
|
||||
if (lookup == map.end()) {
|
||||
u32 new_idx = pats.size();
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "goalc/build_level/collide_bvh.h"
|
||||
#include "collide_bvh.h"
|
||||
|
||||
struct CollideFragMeshData {
|
||||
math::Vector4f bsphere; // not part of the collide frag, but is part of the drawable wrapping it
|
||||
@@ -15,7 +15,7 @@ struct CollideFragMeshData {
|
||||
|
||||
struct CollideFragMeshDataArray {
|
||||
std::vector<CollideFragMeshData> packed_frag_data;
|
||||
std::vector<PatSurface> pats;
|
||||
std::vector<jak1::PatSurface> pats;
|
||||
};
|
||||
|
||||
CollideFragMeshDataArray pack_collide_frags(const std::vector<collide::CollideFrag>& frag_data);
|
||||
@@ -1,112 +0,0 @@
|
||||
#pragma once
|
||||
#include "common/common_types.h"
|
||||
#include "common/math/Vector.h"
|
||||
|
||||
struct PatSurface {
|
||||
enum class Mode { GROUND = 0, WALL = 1, OBSTACLE = 2, MAX_MODE = 3 };
|
||||
enum class Material {
|
||||
STONE = 0,
|
||||
ICE = 1,
|
||||
QUICKSAND = 2,
|
||||
WATERBOTTOM = 3,
|
||||
TAR = 4,
|
||||
SAND = 5,
|
||||
WOOD = 6,
|
||||
GRASS = 7,
|
||||
PCMETAL = 8,
|
||||
SNOW = 9,
|
||||
DEEPSNOW = 10,
|
||||
HOTCOALS = 11,
|
||||
LAVA = 12,
|
||||
CRWOOD = 13,
|
||||
GRAVEL = 14,
|
||||
DIRT = 15,
|
||||
METAL = 16,
|
||||
STRAW = 17,
|
||||
TUBE = 18,
|
||||
SWAMP = 19,
|
||||
STOPPROJ = 20,
|
||||
ROTATE = 21,
|
||||
NEUTRAL = 22,
|
||||
MAX_MATERIAL = 23
|
||||
};
|
||||
|
||||
enum class Event {
|
||||
NONE = 0,
|
||||
DEADLY = 1,
|
||||
ENDLESSFALL = 2,
|
||||
BURN = 3,
|
||||
DEADLYUP = 4,
|
||||
BURNUP = 5,
|
||||
MELT = 6,
|
||||
MAX_EVENT = 7,
|
||||
};
|
||||
|
||||
void set_noentity(bool x) {
|
||||
if (x) {
|
||||
val |= (1 << 0);
|
||||
} else {
|
||||
val &= ~(1 << 0);
|
||||
}
|
||||
}
|
||||
bool get_noentity() const { return val & (1 << 0); }
|
||||
|
||||
void set_nocamera(bool x) {
|
||||
if (x) {
|
||||
val |= (1 << 1);
|
||||
} else {
|
||||
val &= ~(1 << 1);
|
||||
}
|
||||
}
|
||||
bool get_nocamera() const { return val & (1 << 1); }
|
||||
|
||||
void set_noedge(bool x) {
|
||||
if (x) {
|
||||
val |= (1 << 2);
|
||||
} else {
|
||||
val &= ~(1 << 2);
|
||||
}
|
||||
}
|
||||
bool get_noedge() const { return val & (1 << 2); }
|
||||
|
||||
void set_mode(Mode mode) {
|
||||
val &= ~(0b111 << 3);
|
||||
val |= ((u32)mode << 3);
|
||||
}
|
||||
Mode get_mode() const { return (Mode)(0b111 & (val >> 3)); }
|
||||
|
||||
void set_material(Material mat) {
|
||||
val &= ~(0b111111 << 6);
|
||||
val |= ((u32)mat << 6);
|
||||
}
|
||||
Material get_material() const { return (Material)(0b111111 & (val >> 6)); }
|
||||
|
||||
void set_nolineofsight(bool x) {
|
||||
if (x) {
|
||||
val |= (1 << 12);
|
||||
} else {
|
||||
val &= ~(1 << 12);
|
||||
}
|
||||
}
|
||||
bool get_nolineofsight() const { return val & (1 << 12); }
|
||||
|
||||
void set_event(Event ev) {
|
||||
val &= ~(0b111111 << 14);
|
||||
val |= ((u32)ev << 14);
|
||||
}
|
||||
Event get_event() const { return (Event)(0b111111 & (val >> 14)); }
|
||||
|
||||
bool operator==(const PatSurface& other) const { return val == other.val; }
|
||||
// bits 13, [15-31] are unused, or have unknown purpose.
|
||||
u32 val = 0;
|
||||
};
|
||||
|
||||
struct CollideVertex {
|
||||
float x, y, z;
|
||||
};
|
||||
|
||||
struct CollideFace {
|
||||
math::Vector4f bsphere;
|
||||
math::Vector3f v[3];
|
||||
PatSurface pat;
|
||||
};
|
||||
@@ -0,0 +1,96 @@
|
||||
#include "Entity.h"
|
||||
|
||||
math::Vector4f vectorm3_from_json(const nlohmann::json& json) {
|
||||
ASSERT(json.size() == 3);
|
||||
math::Vector4f result;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
result[i] = json[i].get<float>() * METER_LENGTH;
|
||||
}
|
||||
result[3] = 1.f;
|
||||
return result;
|
||||
}
|
||||
|
||||
math::Vector4f vectorm4_from_json(const nlohmann::json& json) {
|
||||
ASSERT(json.size() == 4);
|
||||
math::Vector4f result;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
result[i] = json[i].get<float>() * METER_LENGTH;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
math::Vector4f movie_pos_from_json(const nlohmann::json& json) {
|
||||
ASSERT(json.size() == 4);
|
||||
math::Vector4f result;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
result[i] = json[i].get<float>() * METER_LENGTH;
|
||||
}
|
||||
result[3] = json[3].get<float>() * DEGREES_LENGTH;
|
||||
return result;
|
||||
}
|
||||
|
||||
math::Vector4f vector_from_json(const nlohmann::json& json) {
|
||||
ASSERT(json.size() == 4);
|
||||
math::Vector4f result;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
result[i] = json[i].get<float>();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<Res> res_from_json_array(const std::string& name,
|
||||
const nlohmann::json& json_array) {
|
||||
ASSERT(json_array.size() > 0);
|
||||
std::string array_type = json_array[0].get<std::string>();
|
||||
if (array_type == "int32") {
|
||||
std::vector<s32> data;
|
||||
for (size_t i = 1; i < json_array.size(); i++) {
|
||||
data.push_back(json_array[i].get<int>());
|
||||
}
|
||||
return std::make_unique<ResInt32>(name, data, -1000000000.0000);
|
||||
} else if (array_type == "uint32") {
|
||||
std::vector<u32> data;
|
||||
for (size_t i = 1; i < json_array.size(); i++) {
|
||||
data.push_back(json_array[i].get<u32>());
|
||||
}
|
||||
return std::make_unique<ResUint32>(name, data, -1000000000.0000);
|
||||
} else if (array_type == "vector") {
|
||||
std::vector<math::Vector4f> data;
|
||||
for (size_t i = 1; i < json_array.size(); i++) {
|
||||
data.push_back(vector_from_json(json_array[i]));
|
||||
}
|
||||
return std::make_unique<ResVector>(name, data, -1000000000.0000);
|
||||
} else if (array_type == "vector4m") {
|
||||
std::vector<math::Vector4f> data;
|
||||
for (size_t i = 1; i < json_array.size(); i++) {
|
||||
data.push_back(vectorm4_from_json(json_array[i]));
|
||||
}
|
||||
return std::make_unique<ResVector>(name, data, -1000000000.0000);
|
||||
} else if (array_type == "movie-pos") {
|
||||
std::vector<math::Vector4f> data;
|
||||
for (size_t i = 1; i < json_array.size(); i++) {
|
||||
data.push_back(movie_pos_from_json(json_array[i]));
|
||||
}
|
||||
return std::make_unique<ResVector>(name, data, -1000000000.0000);
|
||||
} else if (array_type == "float") {
|
||||
std::vector<float> data;
|
||||
for (size_t i = 1; i < json_array.size(); i++) {
|
||||
data.push_back(json_array[i].get<float>());
|
||||
}
|
||||
return std::make_unique<ResFloat>(name, data, -1000000000.0000);
|
||||
} else if (array_type == "meters") {
|
||||
std::vector<float> data;
|
||||
for (size_t i = 1; i < json_array.size(); i++) {
|
||||
data.push_back(json_array[i].get<float>() * METER_LENGTH);
|
||||
}
|
||||
return std::make_unique<ResFloat>(name, data, -1000000000.0000);
|
||||
} else if (array_type == "degrees") {
|
||||
std::vector<float> data;
|
||||
for (size_t i = 1; i < json_array.size(); i++) {
|
||||
data.push_back(json_array[i].get<float>() * DEGREES_LENGTH);
|
||||
}
|
||||
return std::make_unique<ResFloat>(name, data, -1000000000.0000);
|
||||
} else {
|
||||
ASSERT_MSG(false, fmt::format("unsupported array type: {}\n", array_type));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/goal_constants.h"
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "goalc/build_level/common/ResLump.h"
|
||||
#include "goalc/data_compiler/DataObjectGenerator.h"
|
||||
|
||||
#include "third-party/json.hpp"
|
||||
|
||||
math::Vector4f vectorm3_from_json(const nlohmann::json& json);
|
||||
math::Vector4f vectorm4_from_json(const nlohmann::json& json);
|
||||
math::Vector4f vector_from_json(const nlohmann::json& json);
|
||||
std::unique_ptr<Res> res_from_json_array(const std::string& name, const nlohmann::json& json_array);
|
||||
@@ -18,15 +18,3 @@ size_t FileInfo::add_to_object_file(DataObjectGenerator& gen) const {
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
FileInfo make_file_info_for_level(const std::string& file_name) {
|
||||
FileInfo info;
|
||||
info.file_type = "bsp-header";
|
||||
info.file_name = file_name;
|
||||
info.major_version = versions::jak1::LEVEL_FILE_VERSION;
|
||||
info.minor_version = 0;
|
||||
info.maya_file_name = "Unknown";
|
||||
info.tool_debug = "Created by OpenGOAL buildlevel";
|
||||
info.mdb_file_name = "Unknown";
|
||||
return info;
|
||||
}
|
||||
@@ -23,6 +23,4 @@ struct FileInfo {
|
||||
std::string mdb_file_name;
|
||||
|
||||
size_t add_to_object_file(DataObjectGenerator& gen) const;
|
||||
};
|
||||
|
||||
FileInfo make_file_info_for_level(const std::string& file_name);
|
||||
};
|
||||
@@ -117,6 +117,28 @@ int ResUint8::get_alignment() const {
|
||||
return 16;
|
||||
}
|
||||
|
||||
ResUint32::ResUint32(const std::string& name, const std::vector<u32>& values, float key_frame)
|
||||
: Res(name, key_frame), m_values(values) {}
|
||||
|
||||
TagInfo ResUint32::get_tag_info() const {
|
||||
TagInfo result;
|
||||
result.elt_type = "uint32";
|
||||
result.elt_count = m_values.size();
|
||||
result.inlined = true;
|
||||
result.data_size = m_values.size() * sizeof(u32);
|
||||
return result;
|
||||
}
|
||||
|
||||
void ResUint32::write_data(DataObjectGenerator& gen) const {
|
||||
for (auto& val : m_values) {
|
||||
gen.add_word(val);
|
||||
}
|
||||
}
|
||||
|
||||
int ResUint32::get_alignment() const {
|
||||
return 16;
|
||||
}
|
||||
|
||||
ResVector::ResVector(const std::string& name,
|
||||
const std::vector<math::Vector4f>& values,
|
||||
float key_frame)
|
||||
@@ -77,6 +77,17 @@ class ResUint8 : public Res {
|
||||
std::vector<u8> m_values;
|
||||
};
|
||||
|
||||
class ResUint32 : public Res {
|
||||
public:
|
||||
ResUint32(const std::string& name, const std::vector<u32>& values, float key_frame);
|
||||
TagInfo get_tag_info() const override;
|
||||
void write_data(DataObjectGenerator& gen) const override;
|
||||
int get_alignment() const override;
|
||||
|
||||
private:
|
||||
std::vector<u32> m_values;
|
||||
};
|
||||
|
||||
class ResVector : public Res {
|
||||
public:
|
||||
ResVector(const std::string& name, const std::vector<math::Vector4f>& values, float key_frame);
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "gltf_mesh_extract.h"
|
||||
|
||||
#include "common/custom_data/pack_helpers.h"
|
||||
|
||||
#include "goalc/build_level/gltf_mesh_extract.h"
|
||||
#include "goalc/data_compiler/DataObjectGenerator.h"
|
||||
|
||||
void tfrag_from_gltf(const gltf_mesh_extract::TfragOutput& mesh_extract_out,
|
||||
@@ -2,10 +2,11 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "gltf_mesh_extract.h"
|
||||
|
||||
#include "common/custom_data/Tfrag3Data.h"
|
||||
|
||||
#include "goalc/build_level/TexturePool.h"
|
||||
#include "goalc/build_level/gltf_mesh_extract.h"
|
||||
#include "goalc/build_level/common/TexturePool.h"
|
||||
|
||||
class DataObjectGenerator;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "build_level.h"
|
||||
|
||||
void save_pc_data(const std::string& nickname,
|
||||
tfrag3::Level& data,
|
||||
const fs::path& fr3_output_dir) {
|
||||
Serializer ser;
|
||||
data.serialize(ser);
|
||||
auto compressed =
|
||||
compression::compress_zstd(ser.get_save_result().first, ser.get_save_result().second);
|
||||
lg::print("stats for {}\n", data.level_name);
|
||||
print_memory_usage(data, ser.get_save_result().second);
|
||||
lg::print("compressed: {} -> {} ({:.2f}%)\n", ser.get_save_result().second, compressed.size(),
|
||||
100.f * compressed.size() / ser.get_save_result().second);
|
||||
file_util::write_binary_file(fr3_output_dir / fmt::format("{}.fr3", str_util::to_upper(nickname)),
|
||||
compressed.data(), compressed.size());
|
||||
}
|
||||
|
||||
std::vector<std::string> get_build_level_deps(const std::string& input_file) {
|
||||
auto level_json = parse_commented_json(
|
||||
file_util::read_text_file(file_util::get_file_path({input_file})), input_file);
|
||||
return {level_json.at("gltf_file").get<std::string>()};
|
||||
}
|
||||
|
||||
// Find all art groups the custom level needs in a list of object files,
|
||||
// skipping any that we already found in other dgos before
|
||||
std::vector<decompiler::ObjectFileRecord> find_art_groups(
|
||||
std::vector<std::string>& processed_ags,
|
||||
const std::vector<std::string>& custom_level_ag,
|
||||
const std::vector<decompiler::ObjectFileRecord>& dgo_files) {
|
||||
std::vector<decompiler::ObjectFileRecord> art_groups;
|
||||
for (const auto& file : dgo_files) {
|
||||
// skip any art groups we already added from other dgos
|
||||
if (std::find(processed_ags.begin(), processed_ags.end(), file.name) != processed_ags.end()) {
|
||||
continue;
|
||||
}
|
||||
if (std::find(custom_level_ag.begin(), custom_level_ag.end(), file.name) !=
|
||||
custom_level_ag.end()) {
|
||||
art_groups.push_back(file);
|
||||
processed_ags.push_back(file.name);
|
||||
}
|
||||
}
|
||||
return art_groups;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/compress.h"
|
||||
#include "common/util/json_util.h"
|
||||
#include "common/util/string_util.h"
|
||||
|
||||
#include "decompiler/level_extractor/extract_level.h"
|
||||
|
||||
void save_pc_data(const std::string& nickname, tfrag3::Level& data, const fs::path& fr3_output_dir);
|
||||
std::vector<std::string> get_build_level_deps(const std::string& input_file);
|
||||
std::vector<decompiler::ObjectFileRecord> find_art_groups(
|
||||
std::vector<std::string>& processed_ags,
|
||||
const std::vector<std::string>& custom_level_ag,
|
||||
const std::vector<decompiler::ObjectFileRecord>& dgo_files);
|
||||
+15
-15
@@ -6,12 +6,12 @@
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "color_quantization.h"
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/math/geometry.h"
|
||||
#include "common/util/Timer.h"
|
||||
|
||||
#include "goalc/build_level/color_quantization.h"
|
||||
|
||||
#include "third-party/tiny_gltf/tiny_gltf.h"
|
||||
|
||||
namespace gltf_mesh_extract {
|
||||
@@ -609,7 +609,7 @@ void extract(const Input& in,
|
||||
dedup_vertices(out);
|
||||
}
|
||||
|
||||
std::optional<std::vector<CollideFace>> subdivide_face_if_needed(CollideFace face_in) {
|
||||
std::optional<std::vector<jak1::CollideFace>> subdivide_face_if_needed(jak1::CollideFace face_in) {
|
||||
math::Vector3f v_min = face_in.v[0];
|
||||
v_min.min_in_place(face_in.v[1]);
|
||||
v_min.min_in_place(face_in.v[2]);
|
||||
@@ -629,7 +629,7 @@ std::optional<std::vector<CollideFace>> subdivide_face_if_needed(CollideFace fac
|
||||
math::Vector3f v0 = face_in.v[0];
|
||||
math::Vector3f v1 = face_in.v[1];
|
||||
math::Vector3f v2 = face_in.v[2];
|
||||
CollideFace fs[4];
|
||||
jak1::CollideFace fs[4];
|
||||
fs[0].v[0] = v0;
|
||||
fs[0].v[1] = a;
|
||||
fs[0].v[2] = c;
|
||||
@@ -653,7 +653,7 @@ std::optional<std::vector<CollideFace>> subdivide_face_if_needed(CollideFace fac
|
||||
fs[3].bsphere = math::bsphere_of_triangle(fs[3].v);
|
||||
fs[3].pat = face_in.pat;
|
||||
|
||||
std::vector<CollideFace> result;
|
||||
std::vector<jak1::CollideFace> result;
|
||||
for (auto f : fs) {
|
||||
auto next_faces = subdivide_face_if_needed(f);
|
||||
if (next_faces) {
|
||||
@@ -671,7 +671,7 @@ std::optional<std::vector<CollideFace>> subdivide_face_if_needed(CollideFace fac
|
||||
struct PatResult {
|
||||
bool set = false;
|
||||
bool ignore = false;
|
||||
PatSurface pat;
|
||||
jak1::PatSurface pat;
|
||||
};
|
||||
|
||||
PatResult custom_props_to_pat(const tinygltf::Value& val, const std::string& /*debug_name*/) {
|
||||
@@ -691,12 +691,12 @@ PatResult custom_props_to_pat(const tinygltf::Value& val, const std::string& /*d
|
||||
result.ignore = false;
|
||||
|
||||
int mat = val.Get("collide_material").Get<int>();
|
||||
ASSERT(mat < (int)PatSurface::Material::MAX_MATERIAL);
|
||||
result.pat.set_material(PatSurface::Material(mat));
|
||||
ASSERT(mat < (int)jak1::PatSurface::Material::MAX_MATERIAL);
|
||||
result.pat.set_material(jak1::PatSurface::Material(mat));
|
||||
|
||||
int evt = val.Get("collide_event").Get<int>();
|
||||
ASSERT(evt < (int)PatSurface::Event::MAX_EVENT);
|
||||
result.pat.set_event(PatSurface::Event(evt));
|
||||
ASSERT(evt < (int)jak1::PatSurface::Event::MAX_EVENT);
|
||||
result.pat.set_event(jak1::PatSurface::Event(evt));
|
||||
|
||||
if (val.Get("nolineofsight").Get<int>()) {
|
||||
result.pat.set_nolineofsight(true);
|
||||
@@ -708,8 +708,8 @@ PatResult custom_props_to_pat(const tinygltf::Value& val, const std::string& /*d
|
||||
|
||||
if (val.Has("collide_mode")) {
|
||||
int mode = val.Get("collide_mode").Get<int>();
|
||||
ASSERT(mode < (int)PatSurface::Mode::MAX_MODE);
|
||||
result.pat.set_mode(PatSurface::Mode(mode));
|
||||
ASSERT(mode < (int)jak1::PatSurface::Mode::MAX_MODE);
|
||||
result.pat.set_mode(jak1::PatSurface::Mode(mode));
|
||||
}
|
||||
|
||||
if (val.Get("nocamera").Get<int>()) {
|
||||
@@ -760,7 +760,7 @@ void extract(const Input& in,
|
||||
auto verts = gltf_vertices(model, prim.attributes, n.w_T_node, false, true, mesh.name);
|
||||
|
||||
for (size_t iidx = 0; iidx < prim_indices.size(); iidx += 3) {
|
||||
CollideFace face;
|
||||
jak1::CollideFace face;
|
||||
|
||||
// get the positions
|
||||
for (int j = 0; j < 3; j++) {
|
||||
@@ -804,7 +804,7 @@ void extract(const Input& in,
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<CollideFace> fixed_faces;
|
||||
std::vector<jak1::CollideFace> fixed_faces;
|
||||
int fix_count = 0;
|
||||
for (auto& face : out.faces) {
|
||||
auto try_fix = subdivide_face_if_needed(face);
|
||||
@@ -835,7 +835,7 @@ void extract(const Input& in,
|
||||
math::Vector3f face_normal =
|
||||
(face.v[1] - face.v[0]).cross(face.v[2] - face.v[0]).normalized();
|
||||
if (face_normal[1] < wall_cos) {
|
||||
face.pat.set_mode(PatSurface::Mode::WALL);
|
||||
face.pat.set_mode(jak1::PatSurface::Mode::WALL);
|
||||
wall_count++;
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -4,8 +4,8 @@
|
||||
|
||||
#include "common/custom_data/Tfrag3Data.h"
|
||||
|
||||
#include "goalc/build_level/TexturePool.h"
|
||||
#include "goalc/build_level/collide_common.h"
|
||||
#include "goalc/build_level/collide/common/collide_common.h"
|
||||
#include "goalc/build_level/common/TexturePool.h"
|
||||
|
||||
namespace gltf_mesh_extract {
|
||||
|
||||
@@ -25,7 +25,7 @@ struct TfragOutput {
|
||||
};
|
||||
|
||||
struct CollideOutput {
|
||||
std::vector<CollideFace> faces;
|
||||
std::vector<jak1::CollideFace> faces;
|
||||
};
|
||||
|
||||
struct Output {
|
||||
@@ -1,13 +1,6 @@
|
||||
#include "Entity.h"
|
||||
|
||||
#include "common/goal_constants.h"
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "goalc/build_level/ResLump.h"
|
||||
#include "goalc/data_compiler/DataObjectGenerator.h"
|
||||
|
||||
#include "third-party/json.hpp"
|
||||
|
||||
namespace jak1 {
|
||||
size_t EntityActor::generate(DataObjectGenerator& gen) const {
|
||||
size_t result = res_lump.generate_header(gen, "entity-actor");
|
||||
for (int i = 0; i < 4; i++) {
|
||||
@@ -30,18 +23,6 @@ size_t EntityActor::generate(DataObjectGenerator& gen) const {
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t EntityAmbient::generate(DataObjectGenerator& gen) const {
|
||||
size_t result = res_lump.generate_header(gen, "entity-ambient");
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
gen.add_word_float(trans[i]);
|
||||
}
|
||||
ASSERT(vis_id < UINT16_MAX);
|
||||
gen.add_word(aid);
|
||||
gen.add_word(vis_id);
|
||||
res_lump.generate_tag_list_and_data(gen, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t generate_drawable_actor(DataObjectGenerator& gen,
|
||||
const EntityActor& actor,
|
||||
size_t actor_loc) {
|
||||
@@ -58,21 +39,6 @@ size_t generate_drawable_actor(DataObjectGenerator& gen,
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t generate_drawable_ambient(DataObjectGenerator& gen,
|
||||
const EntityAmbient& ambient,
|
||||
size_t ambient_loc) {
|
||||
gen.align_to_basic();
|
||||
gen.add_type_tag("drawable-ambient"); // 0
|
||||
size_t result = gen.current_offset_bytes();
|
||||
gen.add_word(ambient.vis_id); // 4
|
||||
gen.link_word_to_byte(gen.add_word(0), ambient_loc); // 8
|
||||
gen.add_word(0); // 12
|
||||
for (int i = 0; i < 4; i++) {
|
||||
gen.add_word_float(ambient.bsphere[i]); // 16, 20, 24, 28
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t generate_inline_array_actors(DataObjectGenerator& gen,
|
||||
const std::vector<EntityActor>& actors) {
|
||||
std::vector<size_t> actor_locs;
|
||||
@@ -101,130 +67,6 @@ size_t generate_inline_array_actors(DataObjectGenerator& gen,
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t generate_inline_array_ambients(DataObjectGenerator& gen,
|
||||
const std::vector<EntityAmbient>& ambients) {
|
||||
std::vector<size_t> ambient_locs;
|
||||
for (auto& ambient : ambients) {
|
||||
ambient_locs.push_back(ambient.generate(gen));
|
||||
}
|
||||
gen.align_to_basic();
|
||||
gen.add_type_tag("drawable-inline-array-ambient"); // 0
|
||||
size_t result = gen.current_offset_bytes();
|
||||
ASSERT(ambients.size() < UINT16_MAX);
|
||||
gen.add_word(ambients.size() << 16); // 4
|
||||
gen.add_word(0);
|
||||
gen.add_word(0);
|
||||
gen.add_word(0);
|
||||
gen.add_word(0);
|
||||
gen.add_word(0);
|
||||
gen.add_word(0);
|
||||
ASSERT((gen.current_offset_bytes() % 16) == 0);
|
||||
for (size_t i = 0; i < ambients.size(); i++) {
|
||||
generate_drawable_ambient(gen, ambients[i], ambient_locs[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace {
|
||||
math::Vector4f vectorm3_from_json(const nlohmann::json& json) {
|
||||
ASSERT(json.size() == 3);
|
||||
math::Vector4f result;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
result[i] = json[i].get<float>() * METER_LENGTH;
|
||||
}
|
||||
result[3] = 1.f;
|
||||
return result;
|
||||
}
|
||||
|
||||
math::Vector4f vectorm4_from_json(const nlohmann::json& json) {
|
||||
ASSERT(json.size() == 4);
|
||||
math::Vector4f result;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
result[i] = json[i].get<float>() * METER_LENGTH;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
math::Vector4f vector_from_json(const nlohmann::json& json) {
|
||||
ASSERT(json.size() == 4);
|
||||
math::Vector4f result;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
result[i] = json[i].get<float>();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<Res> res_from_json_array(const std::string& name,
|
||||
const nlohmann::json& json_array) {
|
||||
ASSERT(json_array.size() > 0);
|
||||
std::string array_type = json_array[0].get<std::string>();
|
||||
if (array_type == "int32") {
|
||||
std::vector<s32> data;
|
||||
for (size_t i = 1; i < json_array.size(); i++) {
|
||||
data.push_back(json_array[i].get<int>());
|
||||
}
|
||||
return std::make_unique<ResInt32>(name, data, -1000000000.0000);
|
||||
} else if (array_type == "vector") {
|
||||
std::vector<math::Vector4f> data;
|
||||
for (size_t i = 1; i < json_array.size(); i++) {
|
||||
data.push_back(vector_from_json(json_array[i]));
|
||||
}
|
||||
return std::make_unique<ResVector>(name, data, -1000000000.0000);
|
||||
} else if (array_type == "vector4m") {
|
||||
std::vector<math::Vector4f> data;
|
||||
for (size_t i = 1; i < json_array.size(); i++) {
|
||||
data.push_back(vectorm4_from_json(json_array[i]));
|
||||
}
|
||||
return std::make_unique<ResVector>(name, data, -1000000000.0000);
|
||||
} else if (array_type == "float") {
|
||||
std::vector<float> data;
|
||||
for (size_t i = 1; i < json_array.size(); i++) {
|
||||
data.push_back(json_array[i].get<float>());
|
||||
}
|
||||
return std::make_unique<ResFloat>(name, data, -1000000000.0000);
|
||||
} else if (array_type == "meters") {
|
||||
std::vector<float> data;
|
||||
for (size_t i = 1; i < json_array.size(); i++) {
|
||||
data.push_back(json_array[i].get<float>() * METER_LENGTH);
|
||||
}
|
||||
return std::make_unique<ResFloat>(name, data, -1000000000.0000);
|
||||
} else {
|
||||
ASSERT_MSG(false, fmt::format("unsupported array type: {}\n", array_type));
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void add_ambients_from_json(const nlohmann::json& json,
|
||||
std::vector<EntityAmbient>& ambient_list,
|
||||
u32 base_aid) {
|
||||
for (const auto& ambient_json : json) {
|
||||
auto& ambient = ambient_list.emplace_back();
|
||||
ambient.aid = ambient_json.value("aid", base_aid + ambient_list.size());
|
||||
ambient.vis_id = ambient_json.value("vis_id", 0);
|
||||
ambient.trans = vectorm4_from_json(ambient_json.at("trans"));
|
||||
ambient.bsphere = vectorm4_from_json(ambient_json.at("bsphere"));
|
||||
if (ambient_json.find("lump") != ambient_json.end()) {
|
||||
for (auto [key, value] : ambient_json.at("lump").items()) {
|
||||
if (value.is_string()) {
|
||||
std::string value_string = value.get<std::string>();
|
||||
if (value_string.size() > 0 && value_string[0] == '\'') {
|
||||
ambient.res_lump.add_res(
|
||||
std::make_unique<ResSymbol>(key, value_string.substr(1), -1000000000.0000));
|
||||
} else {
|
||||
ambient.res_lump.add_res(
|
||||
std::make_unique<ResString>(key, value_string, -1000000000.0000));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (value.is_array()) {
|
||||
ambient.res_lump.add_res(res_from_json_array(key, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
ambient.res_lump.sort_res();
|
||||
}
|
||||
}
|
||||
|
||||
void add_actors_from_json(const nlohmann::json& json,
|
||||
std::vector<EntityActor>& actor_list,
|
||||
u32 base_aid) {
|
||||
@@ -262,4 +104,87 @@ void add_actors_from_json(const nlohmann::json& json,
|
||||
}
|
||||
actor.res_lump.sort_res();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t EntityAmbient::generate(DataObjectGenerator& gen) const {
|
||||
size_t result = res_lump.generate_header(gen, "entity-ambient");
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
gen.add_word_float(trans[i]);
|
||||
}
|
||||
ASSERT(vis_id < UINT16_MAX);
|
||||
gen.add_word(aid);
|
||||
gen.add_word(vis_id);
|
||||
res_lump.generate_tag_list_and_data(gen, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t generate_drawable_ambient(DataObjectGenerator& gen,
|
||||
const EntityAmbient& ambient,
|
||||
size_t ambient_loc) {
|
||||
gen.align_to_basic();
|
||||
gen.add_type_tag("drawable-ambient"); // 0
|
||||
size_t result = gen.current_offset_bytes();
|
||||
gen.add_word(ambient.vis_id); // 4
|
||||
gen.link_word_to_byte(gen.add_word(0), ambient_loc); // 8
|
||||
gen.add_word(0); // 12
|
||||
for (int i = 0; i < 4; i++) {
|
||||
gen.add_word_float(ambient.bsphere[i]); // 16, 20, 24, 28
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t generate_inline_array_ambients(DataObjectGenerator& gen,
|
||||
const std::vector<EntityAmbient>& ambients) {
|
||||
std::vector<size_t> ambient_locs;
|
||||
for (auto& ambient : ambients) {
|
||||
ambient_locs.push_back(ambient.generate(gen));
|
||||
}
|
||||
gen.align_to_basic();
|
||||
gen.add_type_tag("drawable-inline-array-ambient"); // 0
|
||||
size_t result = gen.current_offset_bytes();
|
||||
ASSERT(ambients.size() < UINT16_MAX);
|
||||
gen.add_word(ambients.size() << 16); // 4
|
||||
gen.add_word(0);
|
||||
gen.add_word(0);
|
||||
gen.add_word(0);
|
||||
gen.add_word(0);
|
||||
gen.add_word(0);
|
||||
gen.add_word(0);
|
||||
ASSERT((gen.current_offset_bytes() % 16) == 0);
|
||||
for (size_t i = 0; i < ambients.size(); i++) {
|
||||
generate_drawable_ambient(gen, ambients[i], ambient_locs[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void add_ambients_from_json(const nlohmann::json& json,
|
||||
std::vector<EntityAmbient>& ambient_list,
|
||||
u32 base_aid) {
|
||||
for (const auto& ambient_json : json) {
|
||||
auto& ambient = ambient_list.emplace_back();
|
||||
ambient.aid = ambient_json.value("aid", base_aid + ambient_list.size());
|
||||
ambient.vis_id = ambient_json.value("vis_id", 0);
|
||||
ambient.trans = vectorm4_from_json(ambient_json.at("trans"));
|
||||
ambient.bsphere = vectorm4_from_json(ambient_json.at("bsphere"));
|
||||
if (ambient_json.find("lump") != ambient_json.end()) {
|
||||
for (auto [key, value] : ambient_json.at("lump").items()) {
|
||||
if (value.is_string()) {
|
||||
std::string value_string = value.get<std::string>();
|
||||
if (value_string.size() > 0 && value_string[0] == '\'') {
|
||||
ambient.res_lump.add_res(
|
||||
std::make_unique<ResSymbol>(key, value_string.substr(1), -1000000000.0000));
|
||||
} else {
|
||||
ambient.res_lump.add_res(
|
||||
std::make_unique<ResString>(key, value_string, -1000000000.0000));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (value.is_array()) {
|
||||
ambient.res_lump.add_res(res_from_json_array(key, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
ambient.res_lump.sort_res();
|
||||
}
|
||||
}
|
||||
} // namespace jak1
|
||||
@@ -1,18 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "goalc/build_level/ResLump.h"
|
||||
|
||||
#include "third-party/json.hpp"
|
||||
#include "goalc/build_level/common/Entity.h"
|
||||
|
||||
namespace jak1 {
|
||||
/*
|
||||
* (trans vector :inline :offset-assert 32)
|
||||
(aid uint32 :offset-assert 48)
|
||||
* (nav-mesh nav-mesh :offset-assert 52)
|
||||
(etype type :offset-assert 56) ;; probably type
|
||||
(task game-task :offset-assert 60)
|
||||
(vis-id uint16 :offset-assert 62)
|
||||
(vis-id-signed int16 :offset 62) ;; added
|
||||
(quat quaternion :inline :offset-assert 64)
|
||||
* (trans vector :inline :offset-assert 32)
|
||||
* (aid uint32 :offset-assert 48)
|
||||
* (nav-mesh nav-mesh :offset-assert 52)
|
||||
* (etype type :offset-assert 56) ;; probably type
|
||||
* (task game-task :offset-assert 60)
|
||||
* (vis-id uint16 :offset-assert 62)
|
||||
* (vis-id-signed int16 :offset 62) ;; added
|
||||
* (quat quaternion :inline :offset-assert 64)
|
||||
*/
|
||||
struct EntityActor {
|
||||
ResLump res_lump;
|
||||
@@ -28,6 +27,13 @@ struct EntityActor {
|
||||
size_t generate(DataObjectGenerator& gen) const;
|
||||
};
|
||||
|
||||
size_t generate_inline_array_actors(DataObjectGenerator& gen,
|
||||
const std::vector<EntityActor>& actors);
|
||||
|
||||
void add_actors_from_json(const nlohmann::json& json,
|
||||
std::vector<EntityActor>& actor_list,
|
||||
u32 base_aid);
|
||||
|
||||
struct EntityAmbient {
|
||||
ResLump res_lump;
|
||||
u32 aid = 0;
|
||||
@@ -37,15 +43,9 @@ struct EntityAmbient {
|
||||
size_t generate(DataObjectGenerator& gen) const;
|
||||
};
|
||||
|
||||
size_t generate_inline_array_actors(DataObjectGenerator& gen,
|
||||
const std::vector<EntityActor>& actors);
|
||||
|
||||
size_t generate_inline_array_ambients(DataObjectGenerator& gen,
|
||||
const std::vector<EntityAmbient>& ambients);
|
||||
void add_ambients_from_json(const nlohmann::json& json,
|
||||
std::vector<EntityAmbient>& ambient_list,
|
||||
u32 base_aid);
|
||||
|
||||
void add_actors_from_json(const nlohmann::json& json,
|
||||
std::vector<EntityActor>& actor_list,
|
||||
u32 base_aid);
|
||||
} // namespace jak1
|
||||
@@ -0,0 +1,19 @@
|
||||
#include "FileInfo.h"
|
||||
|
||||
#include "common/versions/versions.h"
|
||||
|
||||
#include "goalc/data_compiler/DataObjectGenerator.h"
|
||||
|
||||
namespace jak1 {
|
||||
FileInfo make_file_info_for_level(const std::string& file_name) {
|
||||
FileInfo info;
|
||||
info.file_type = "bsp-header";
|
||||
info.file_name = file_name;
|
||||
info.major_version = versions::jak1::LEVEL_FILE_VERSION;
|
||||
info.minor_version = 0;
|
||||
info.maya_file_name = "Unknown";
|
||||
info.tool_debug = "Created by OpenGOAL buildlevel";
|
||||
info.mdb_file_name = "Unknown";
|
||||
return info;
|
||||
}
|
||||
} // namespace jak1
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "goalc/build_level/common/FileInfo.h"
|
||||
|
||||
namespace jak1 {
|
||||
FileInfo make_file_info_for_level(const std::string& file_name);
|
||||
} // namespace jak1
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "LevelFile.h"
|
||||
|
||||
#include "goalc/data_compiler/DataObjectGenerator.h"
|
||||
|
||||
namespace jak1 {
|
||||
static size_t ambient_arr_slot;
|
||||
|
||||
size_t DrawableTreeArray::add_to_object_file(DataObjectGenerator& gen) const {
|
||||
@@ -85,7 +84,7 @@ std::vector<u8> LevelFile::save_object_file() const {
|
||||
auto file_info_slot = info.add_to_object_file(gen);
|
||||
gen.link_word_to_byte(1, file_info_slot);
|
||||
|
||||
ambient_arr_slot = generate_inline_array_ambients(gen, ambients);
|
||||
ambient_arr_slot = jak1::generate_inline_array_ambients(gen, ambients);
|
||||
|
||||
//(bsphere vector :inline :offset-assert 16)
|
||||
//(all-visible-list (pointer uint16) :offset-assert 32)
|
||||
@@ -124,4 +123,5 @@ std::vector<u8> LevelFile::save_object_file() const {
|
||||
//(unk-data-8 uint32 55 :offset-assert 180)
|
||||
|
||||
return gen.generate_v2();
|
||||
}
|
||||
}
|
||||
} // namespace jak1
|
||||
@@ -4,17 +4,19 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Entity.h"
|
||||
#include "FileInfo.h"
|
||||
#include "ambient.h"
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "goalc/build_level/Entity.h"
|
||||
#include "goalc/build_level/FileInfo.h"
|
||||
#include "goalc/build_level/Tfrag.h"
|
||||
#include "goalc/build_level/ambient.h"
|
||||
#include "goalc/build_level/collide_bvh.h"
|
||||
#include "goalc/build_level/collide_common.h"
|
||||
#include "goalc/build_level/collide_drawable.h"
|
||||
#include "goalc/build_level/collide_pack.h"
|
||||
#include "goalc/build_level/collide/common/collide_common.h"
|
||||
#include "goalc/build_level/collide/jak1/collide_bvh.h"
|
||||
#include "goalc/build_level/collide/jak1/collide_drawable.h"
|
||||
#include "goalc/build_level/collide/jak1/collide_pack.h"
|
||||
#include "goalc/build_level/common/Tfrag.h"
|
||||
|
||||
namespace jak1 {
|
||||
struct VisibilityString {
|
||||
std::vector<u8> bytes;
|
||||
};
|
||||
@@ -134,4 +136,5 @@ struct LevelFile {
|
||||
// (unk-data-8 uint32 55 :offset-assert 180)
|
||||
|
||||
std::vector<u8> save_object_file() const;
|
||||
};
|
||||
};
|
||||
} // namespace jak1
|
||||
@@ -24,6 +24,7 @@
|
||||
)
|
||||
*/
|
||||
|
||||
namespace jak1 {
|
||||
size_t DrawableTreeAmbient::add_to_object_file(DataObjectGenerator& gen, size_t ambient_array) {
|
||||
gen.align_to_basic();
|
||||
gen.add_type_tag("drawable-tree-ambient");
|
||||
@@ -37,4 +38,5 @@ size_t DrawableTreeAmbient::add_to_object_file(DataObjectGenerator& gen, size_t
|
||||
gen.add_word(0); // 28
|
||||
gen.link_word_to_byte(gen.add_word(0), ambient_array);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} // namespace jak1
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
class DataObjectGenerator;
|
||||
|
||||
namespace jak1 {
|
||||
struct DrawableTreeAmbient {
|
||||
static size_t add_to_object_file(DataObjectGenerator& gen, size_t ambient_array);
|
||||
};
|
||||
};
|
||||
} // namespace jak1
|
||||
@@ -1,66 +1,15 @@
|
||||
#include "build_level.h"
|
||||
|
||||
#include "common/custom_data/Tfrag3Data.h"
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/util/compress.h"
|
||||
#include "common/util/json_util.h"
|
||||
#include "common/util/string_util.h"
|
||||
|
||||
#include "decompiler/extractor/extractor_util.h"
|
||||
#include "decompiler/level_extractor/extract_merc.h"
|
||||
#include "goalc/build_level/Entity.h"
|
||||
#include "goalc/build_level/FileInfo.h"
|
||||
#include "goalc/build_level/LevelFile.h"
|
||||
#include "goalc/build_level/Tfrag.h"
|
||||
#include "goalc/build_level/collide_bvh.h"
|
||||
#include "goalc/build_level/collide_pack.h"
|
||||
#include "goalc/build_level/gltf_mesh_extract.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
void save_pc_data(const std::string& nickname,
|
||||
tfrag3::Level& data,
|
||||
const fs::path& fr3_output_dir) {
|
||||
Serializer ser;
|
||||
data.serialize(ser);
|
||||
auto compressed =
|
||||
compression::compress_zstd(ser.get_save_result().first, ser.get_save_result().second);
|
||||
lg::print("stats for {}\n", data.level_name);
|
||||
print_memory_usage(data, ser.get_save_result().second);
|
||||
lg::print("compressed: {} -> {} ({:.2f}%)\n", ser.get_save_result().second, compressed.size(),
|
||||
100.f * compressed.size() / ser.get_save_result().second);
|
||||
file_util::write_binary_file(fr3_output_dir / fmt::format("{}.fr3", str_util::to_upper(nickname)),
|
||||
compressed.data(), compressed.size());
|
||||
}
|
||||
|
||||
std::vector<std::string> get_build_level_deps(const std::string& input_file) {
|
||||
auto level_json = parse_commented_json(
|
||||
file_util::read_text_file(file_util::get_file_path({input_file})), input_file);
|
||||
return {level_json.at("gltf_file").get<std::string>()};
|
||||
}
|
||||
|
||||
// Find all art groups the custom level needs in a list of object files,
|
||||
// skipping any that we already found in other dgos before
|
||||
std::vector<decompiler::ObjectFileRecord> find_art_groups(
|
||||
std::vector<std::string>& processed_ags,
|
||||
const std::vector<std::string>& custom_level_ag,
|
||||
const std::vector<decompiler::ObjectFileRecord>& dgo_files) {
|
||||
std::vector<decompiler::ObjectFileRecord> art_groups;
|
||||
for (const auto& file : dgo_files) {
|
||||
// skip any art groups we already added from other dgos
|
||||
if (std::find(processed_ags.begin(), processed_ags.end(), file.name) != processed_ags.end()) {
|
||||
continue;
|
||||
}
|
||||
if (std::find(custom_level_ag.begin(), custom_level_ag.end(), file.name) !=
|
||||
custom_level_ag.end()) {
|
||||
art_groups.push_back(file);
|
||||
processed_ags.push_back(file.name);
|
||||
}
|
||||
}
|
||||
return art_groups;
|
||||
}
|
||||
#include "goalc/build_level/collide/jak1/collide_bvh.h"
|
||||
#include "goalc/build_level/collide/jak1/collide_pack.h"
|
||||
#include "goalc/build_level/common/Tfrag.h"
|
||||
#include "goalc/build_level/jak1/Entity.h"
|
||||
#include "goalc/build_level/jak1/FileInfo.h"
|
||||
#include "goalc/build_level/jak1/LevelFile.h"
|
||||
|
||||
namespace jak1 {
|
||||
bool run_build_level(const std::string& input_file,
|
||||
const std::string& bsp_output_file,
|
||||
const std::string& output_prefix) {
|
||||
@@ -100,7 +49,8 @@ bool run_build_level(const std::string& input_file,
|
||||
file.actors = std::move(actors);
|
||||
// ambients
|
||||
std::vector<EntityAmbient> ambients;
|
||||
add_ambients_from_json(level_json.at("ambients"), ambients, level_json.value("base_id", 12345));
|
||||
jak1::add_ambients_from_json(level_json.at("ambients"), ambients,
|
||||
level_json.value("base_id", 12345));
|
||||
file.ambients = std::move(ambients);
|
||||
auto& ambient_drawable_tree = file.drawable_trees.ambients.emplace_back();
|
||||
(void)ambient_drawable_tree;
|
||||
@@ -259,3 +209,4 @@ bool run_build_level(const std::string& input_file,
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace jak1
|
||||
@@ -1,11 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "decompiler/level_extractor/extract_level.h"
|
||||
#include "goalc/build_level/common/build_level.h"
|
||||
|
||||
namespace jak1 {
|
||||
bool run_build_level(const std::string& input_file,
|
||||
const std::string& bsp_output_file,
|
||||
const std::string& output_prefix);
|
||||
std::vector<std::string> get_build_level_deps(const std::string& input_file);
|
||||
} // namespace jak1
|
||||
@@ -0,0 +1,109 @@
|
||||
#include "Entity.h"
|
||||
|
||||
namespace jak2 {
|
||||
size_t EntityActor::generate(DataObjectGenerator& gen) const {
|
||||
size_t result = res_lump.generate_header(gen, "entity-actor");
|
||||
for (int i = 0; i < 4; i++) {
|
||||
gen.add_word_float(trans[i]);
|
||||
}
|
||||
gen.add_word(aid);
|
||||
gen.add_word(kill_mask);
|
||||
gen.add_type_tag(etype);
|
||||
|
||||
ASSERT(game_task < UINT16_MAX);
|
||||
ASSERT(vis_id < UINT16_MAX);
|
||||
u32 packed = (game_task) | (vis_id << 16);
|
||||
gen.add_word(packed);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
gen.add_word_float(quat[i]);
|
||||
}
|
||||
|
||||
res_lump.generate_tag_list_and_data(gen, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t generate_drawable_actor(DataObjectGenerator& gen,
|
||||
const EntityActor& actor,
|
||||
size_t actor_loc) {
|
||||
gen.align_to_basic();
|
||||
gen.add_type_tag("drawable-actor"); // 0
|
||||
size_t result = gen.current_offset_bytes();
|
||||
gen.add_word(actor.vis_id); // 4
|
||||
gen.link_word_to_byte(gen.add_word(0), actor_loc); // 8
|
||||
gen.add_word(0); // 12
|
||||
for (int i = 0; i < 4; i++) {
|
||||
gen.add_word_float(actor.bsphere[i]); // 16, 20, 24, 28
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t generate_inline_array_actors(DataObjectGenerator& gen,
|
||||
const std::vector<EntityActor>& actors) {
|
||||
std::vector<size_t> actor_locs;
|
||||
for (auto& actor : actors) {
|
||||
actor_locs.push_back(actor.generate(gen));
|
||||
}
|
||||
|
||||
gen.align_to_basic();
|
||||
gen.add_type_tag("drawable-inline-array-actor"); // 0
|
||||
size_t result = gen.current_offset_bytes();
|
||||
ASSERT(actors.size() < UINT16_MAX);
|
||||
gen.add_word(actors.size() << 16); // 4
|
||||
gen.add_word(0);
|
||||
gen.add_word(0);
|
||||
|
||||
gen.add_word(0);
|
||||
gen.add_word(0);
|
||||
gen.add_word(0);
|
||||
gen.add_word(0);
|
||||
|
||||
ASSERT((gen.current_offset_bytes() % 16) == 0);
|
||||
|
||||
for (size_t i = 0; i < actors.size(); i++) {
|
||||
generate_drawable_actor(gen, actors[i], actor_locs[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void add_actors_from_json(const nlohmann::json& json,
|
||||
std::vector<EntityActor>& actor_list,
|
||||
u32 base_aid) {
|
||||
for (const auto& actor_json : json) {
|
||||
auto& actor = actor_list.emplace_back();
|
||||
actor.aid = actor_json.value("aid", base_aid + actor_list.size());
|
||||
actor.trans = vectorm3_from_json(actor_json.at("trans"));
|
||||
actor.etype = actor_json.at("etype").get<std::string>();
|
||||
actor.kill_mask = actor_json.value("kill_mask", 0);
|
||||
actor.game_task = actor_json.value("game_task", 0);
|
||||
actor.vis_id = actor_json.value("vis_id", 0);
|
||||
actor.quat = math::Vector4f(0, 0, 0, 1);
|
||||
if (actor_json.find("quat") != actor_json.end()) {
|
||||
actor.quat = vector_from_json(actor_json.at("quat"));
|
||||
}
|
||||
actor.bsphere = vectorm4_from_json(actor_json.at("bsphere"));
|
||||
|
||||
if (actor_json.find("lump") != actor_json.end()) {
|
||||
for (auto [key, value] : actor_json.at("lump").items()) {
|
||||
if (value.is_string()) {
|
||||
std::string value_string = value.get<std::string>();
|
||||
if (value_string.size() > 0 && value_string[0] == '\'') {
|
||||
actor.res_lump.add_res(
|
||||
std::make_unique<ResSymbol>(key, value_string.substr(1), -1000000000.0000));
|
||||
} else {
|
||||
actor.res_lump.add_res(
|
||||
std::make_unique<ResString>(key, value_string, -1000000000.0000));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value.is_array()) {
|
||||
actor.res_lump.add_res(res_from_json_array(key, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
actor.res_lump.sort_res();
|
||||
}
|
||||
}
|
||||
} // namespace jak2
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "goalc/build_level/common/Entity.h"
|
||||
|
||||
namespace jak2 {
|
||||
/*
|
||||
* (trans vector :inline :offset-assert 32)
|
||||
* (aid uint32 :offset-assert 48)
|
||||
* (kill-mask task-mask :offset-assert 52)
|
||||
* (etype type :offset-assert 56) ;; probably type
|
||||
* (task game-task :offset-assert 60)
|
||||
* (vis-id uint16 :offset-assert 62)
|
||||
* (quat quaternion :inline :offset-assert 64)
|
||||
*/
|
||||
struct EntityActor {
|
||||
ResLump res_lump;
|
||||
math::Vector4f trans; // w = 1 here
|
||||
u32 aid = 0;
|
||||
u32 kill_mask = 0;
|
||||
std::string etype;
|
||||
u32 game_task = 0;
|
||||
u32 vis_id = 0;
|
||||
math::Vector4f quat;
|
||||
|
||||
math::Vector4f bsphere;
|
||||
|
||||
size_t generate(DataObjectGenerator& gen) const;
|
||||
};
|
||||
|
||||
size_t generate_inline_array_actors(DataObjectGenerator& gen,
|
||||
const std::vector<EntityActor>& actors);
|
||||
|
||||
void add_actors_from_json(const nlohmann::json& json,
|
||||
std::vector<EntityActor>& actor_list,
|
||||
u32 base_aid);
|
||||
|
||||
struct Region {};
|
||||
} // namespace jak2
|
||||
@@ -0,0 +1,19 @@
|
||||
#include "FileInfo.h"
|
||||
|
||||
#include "common/versions/versions.h"
|
||||
|
||||
#include "goalc/data_compiler/DataObjectGenerator.h"
|
||||
|
||||
namespace jak2 {
|
||||
FileInfo make_file_info_for_level(const std::string& file_name) {
|
||||
FileInfo info;
|
||||
info.file_type = "bsp-header";
|
||||
info.file_name = file_name;
|
||||
info.major_version = versions::jak2::LEVEL_FILE_VERSION;
|
||||
info.minor_version = 0;
|
||||
info.maya_file_name = "Unknown";
|
||||
info.tool_debug = "Created by OpenGOAL buildlevel";
|
||||
info.mdb_file_name = "Unknown";
|
||||
return info;
|
||||
}
|
||||
} // namespace jak2
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "goalc/build_level/common/FileInfo.h"
|
||||
|
||||
namespace jak2 {
|
||||
FileInfo make_file_info_for_level(const std::string& file_name);
|
||||
} // namespace jak2
|
||||
@@ -0,0 +1,144 @@
|
||||
#include "LevelFile.h"
|
||||
|
||||
#include "goalc/data_compiler/DataObjectGenerator.h"
|
||||
|
||||
namespace jak2 {
|
||||
size_t DrawableTreeArray::add_to_object_file(DataObjectGenerator& gen) const {
|
||||
/*
|
||||
(deftype drawable-tree-array (drawable-group)
|
||||
((trees drawable-tree 1 :offset 32 :score 100))
|
||||
:flag-assert #x1200000024
|
||||
)
|
||||
(deftype drawable-group (drawable)
|
||||
((length int16 :offset 6)
|
||||
(data drawable 1 :offset-assert 32)
|
||||
)
|
||||
(:methods
|
||||
(new (symbol type int) _type_)
|
||||
)
|
||||
:flag-assert #x1200000024
|
||||
)
|
||||
*/
|
||||
gen.align_to_basic();
|
||||
gen.add_type_tag("drawable-tree-array");
|
||||
size_t result = gen.current_offset_bytes();
|
||||
int num_trees = 0;
|
||||
num_trees += tfrags.size();
|
||||
num_trees += collides.size();
|
||||
gen.add_word(num_trees << 16);
|
||||
gen.add_word(0);
|
||||
gen.add_word(0);
|
||||
|
||||
gen.add_word(0);
|
||||
gen.add_word(0);
|
||||
gen.add_word(0);
|
||||
gen.add_word(0);
|
||||
|
||||
// todo add trees...
|
||||
|
||||
if (num_trees == 0) {
|
||||
gen.add_word(0); // the one at the end.
|
||||
} else {
|
||||
int tree_word = (int)gen.current_offset_bytes() / 4;
|
||||
for (int i = 0; i < num_trees; i++) {
|
||||
gen.add_word(0);
|
||||
}
|
||||
|
||||
for (auto& tfrag : tfrags) {
|
||||
// gen.set_word(tree_word++, tfrag.add_to_object_file(gen));
|
||||
gen.link_word_to_byte(tree_word++, tfrag.add_to_object_file(gen));
|
||||
}
|
||||
|
||||
for (auto& collide : collides) {
|
||||
gen.link_word_to_byte(tree_word++, collide.add_to_object_file(gen));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t generate_u32_array(const std::vector<u32>& array, DataObjectGenerator& gen) {
|
||||
gen.align(4);
|
||||
size_t result = gen.current_offset_bytes();
|
||||
for (auto& entry : array) {
|
||||
gen.add_word(entry);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<u8> LevelFile::save_object_file() const {
|
||||
DataObjectGenerator gen;
|
||||
gen.add_type_tag("bsp-header");
|
||||
|
||||
// add blank space for the bsp-header
|
||||
while (gen.words() < 100) {
|
||||
gen.add_word(0);
|
||||
}
|
||||
|
||||
//(info file-info :offset 4)
|
||||
auto file_info_slot = info.add_to_object_file(gen);
|
||||
gen.link_word_to_byte(1, file_info_slot);
|
||||
|
||||
//(bsphere vector :inline :offset-assert 16)
|
||||
//(all-visible-list (pointer uint16) :offset-assert 32)
|
||||
//(visible-list-length int32 :offset-assert 36)
|
||||
//(drawable-trees drawable-tree-array :offset-assert 40)
|
||||
gen.link_word_to_byte(40 / 4, drawable_trees.add_to_object_file(gen));
|
||||
//(pat pointer :offset-assert 44)
|
||||
//(pat-length int32 :offset-assert 48)
|
||||
//(texture-remap-table (pointer uint64) :offset-assert 52)
|
||||
//(texture-remap-table-len int32 :offset-assert 56)
|
||||
//(texture-ids (pointer texture-id) :offset-assert 60)
|
||||
//(texture-page-count int32 :offset-assert 64)
|
||||
//(unknown-basic basic :offset-assert 68)
|
||||
//(name symbol :offset-assert 72)
|
||||
gen.link_word_to_symbol(name, 72 / 4);
|
||||
//(nickname symbol :offset-assert 76)
|
||||
gen.link_word_to_symbol(nickname, 76 / 4);
|
||||
//(vis-info level-vis-info 8 :offset-assert 80)
|
||||
//(actors drawable-inline-array-actor :offset-assert 112)
|
||||
gen.link_word_to_byte(112 / 4, generate_inline_array_actors(gen, actors));
|
||||
//(cameras (array entity-camera) :offset-assert 116)
|
||||
//(nodes (inline-array bsp-node) :offset-assert 120)
|
||||
//(level level :offset-assert 124)
|
||||
//(current-leaf-idx uint16 :offset-assert 128)
|
||||
//(cam-outside-bsp uint8 :offset 152)
|
||||
//(cam-using-back uint8 :offset-assert 153)
|
||||
//(cam-box-idx uint16 :offset-assert 154)
|
||||
//(ambients symbol :offset-assert 156)
|
||||
//(subdivide-close float :offset-assert 160)
|
||||
//(subdivide-far float :offset-assert 160)
|
||||
//(race-meshes (array entity-race-mesh) :offset-assert 168)
|
||||
//(actor-birth-order (pointer uint32) :offset-assert 172)
|
||||
gen.link_word_to_byte(172 / 4, generate_u32_array(actor_birth_order, gen));
|
||||
//(light-hash light-hash :offset-assert 176)
|
||||
//(nav-meshes (array entity-nav-mesh) :offset-assert 180)
|
||||
//(actor-groups (array actor-group) :offset-assert 184)
|
||||
//(region-trees (array drawable-tree-region-prim) :offset-assert 188)
|
||||
//(region-array region-array :offset-assert 192)
|
||||
//(collide-hash collide-hash :offset-assert 196)
|
||||
//(wind-array uint32 :offset 200)
|
||||
//(wind-array-length int32 :offset 204)
|
||||
//(city-level-info city-level-info :offset 208)
|
||||
//(vis-spheres vector-array :offset 216)
|
||||
//(vis-spheres-length uint32 :offset 248)
|
||||
//(region-tree drawable-tree-region-prim :offset 252)
|
||||
//(tfrag-masks texture-masks-array :offset-assert 256)
|
||||
//(tfrag-closest (pointer float) :offset-assert 260)
|
||||
//(tfrag-mask-count uint32 :offset 260)
|
||||
//(shrub-masks texture-masks-array :offset-assert 264)
|
||||
//(shrub-closest (pointer float) :offset-assert 268)
|
||||
//(shrub-mask-count uint32 :offset 268)
|
||||
//(alpha-masks texture-masks-array :offset-assert 272)
|
||||
//(alpha-closest (pointer float) :offset-assert 276)
|
||||
//(alpha-mask-count uint32 :offset 276)
|
||||
//(water-masks texture-masks-array :offset-assert 280)
|
||||
//(water-closest (pointer float) :offset-assert 284)
|
||||
//(water-mask-count uint32 :offset 284)
|
||||
//(bsp-scale vector :inline :offset-assert 288)
|
||||
//(bsp-offset vector :inline :offset-assert 304)
|
||||
//(end uint8 :offset 399)
|
||||
|
||||
return gen.generate_v2();
|
||||
}
|
||||
} // namespace jak2
|
||||
@@ -0,0 +1,187 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "goalc/build_level/collide/common/collide_common.h"
|
||||
#include "goalc/build_level/collide/jak1/collide_bvh.h"
|
||||
#include "goalc/build_level/collide/jak1/collide_drawable.h"
|
||||
#include "goalc/build_level/collide/jak1/collide_pack.h"
|
||||
#include "goalc/build_level/common/Tfrag.h"
|
||||
#include "goalc/build_level/jak2/Entity.h"
|
||||
#include "goalc/build_level/jak2/FileInfo.h"
|
||||
|
||||
namespace jak2 {
|
||||
struct VisibilityString {
|
||||
std::vector<u8> bytes;
|
||||
};
|
||||
|
||||
struct DrawableTreeInstanceTie {};
|
||||
|
||||
struct DrawableTreeActor {};
|
||||
|
||||
struct DrawableTreeInstanceShrub {};
|
||||
|
||||
struct DrawableTreeRegionPrim {};
|
||||
|
||||
struct DrawableTreeArray {
|
||||
std::vector<DrawableTreeTfrag> tfrags;
|
||||
std::vector<DrawableTreeInstanceTie> ties;
|
||||
std::vector<DrawableTreeActor> actors; // unused?
|
||||
std::vector<DrawableTreeRegionPrim> regions;
|
||||
std::vector<DrawableTreeCollideFragment> collides;
|
||||
std::vector<DrawableTreeInstanceShrub> shrubs;
|
||||
size_t add_to_object_file(DataObjectGenerator& gen) const;
|
||||
};
|
||||
|
||||
struct TextureRemap {};
|
||||
|
||||
struct TextureId {};
|
||||
|
||||
struct VisInfo {};
|
||||
|
||||
struct EntityCamera {};
|
||||
|
||||
struct BspNode {};
|
||||
|
||||
struct RaceMesh {};
|
||||
|
||||
struct LightHash {};
|
||||
|
||||
struct EntityNavMesh {};
|
||||
|
||||
struct ActorGroup {};
|
||||
|
||||
struct RegionTree {};
|
||||
|
||||
struct RegionArray {};
|
||||
|
||||
struct CollideHash {};
|
||||
|
||||
struct CityLevelInfo {};
|
||||
|
||||
struct TextureMasksArray {};
|
||||
|
||||
// This is a place to collect all the data that should go into the bsp-header file.
|
||||
struct LevelFile {
|
||||
// (info file-info :offset 4)
|
||||
FileInfo info;
|
||||
|
||||
// (all-visible-list (pointer uint16) :offset-assert 32)
|
||||
// (visible-list-length int16 :offset-assert 36)
|
||||
// (extra-vis-list-length int16 :offset-assert 38)
|
||||
VisibilityString all_visibile_list;
|
||||
|
||||
// (drawable-trees drawable-tree-array :offset-assert 40)
|
||||
DrawableTreeArray drawable_trees;
|
||||
|
||||
// (pat pointer :offset-assert 44)
|
||||
// (pat-length int32 :offset-assert 48)
|
||||
std::vector<PatSurface> pat;
|
||||
|
||||
// (texture-remap-table (pointer uint64) :offset-assert 52)
|
||||
// (texture-remap-table-len int32 :offset-assert 56)
|
||||
std::vector<TextureRemap> texture_remap_table;
|
||||
|
||||
// (texture-ids (pointer texture-id) :offset-assert 60)
|
||||
// (texture-page-count int32 :offset-assert 64)
|
||||
std::vector<TextureId> texture_ids;
|
||||
|
||||
// (unknown-basic basic :offset-assert 68)
|
||||
// "misc", seems like it can be zero and is unused.
|
||||
|
||||
// (name symbol :offset-assert 72)
|
||||
std::string name; // full name
|
||||
|
||||
// (nickname symbol :offset-assert 76)
|
||||
std::string nickname; // 3 char name
|
||||
|
||||
// (vis-info level-vis-info 8 :offset-assert 80) ;; note: 0 when
|
||||
std::array<VisInfo, 8> vis_infos;
|
||||
|
||||
// (actors drawable-inline-array-actor :offset-assert 112)
|
||||
std::vector<EntityActor> actors;
|
||||
|
||||
// (cameras (array entity-camera) :offset-assert 116)
|
||||
std::vector<EntityCamera> cameras;
|
||||
|
||||
// (nodes (inline-array bsp-node) :offset-assert 120)
|
||||
std::vector<BspNode> nodes;
|
||||
|
||||
// (level level :offset-assert 124)
|
||||
// zero
|
||||
|
||||
// (current-leaf-idx uint16 :offset-assert 128)
|
||||
// zero
|
||||
|
||||
// (texture-flags texture-page-flag 10 :offset-assert 130)
|
||||
std::vector<u16> texture_flags;
|
||||
|
||||
// (cam-outside-bsp uint8 :offset 152)
|
||||
// (cam-using-back uint8 :offset-assert 153)
|
||||
// (cam-box-idx uint16 :offset-assert 154)
|
||||
// zero
|
||||
|
||||
// (ambients symbol :offset-assert 156)
|
||||
// #t
|
||||
|
||||
// (subdivide-close float :offset-assert 160)
|
||||
float close_subdiv = 0;
|
||||
|
||||
// (subdivide-far float :offset-assert 164)
|
||||
float far_subdiv = 0;
|
||||
|
||||
// (race-meshes (array entity-race-mesh) :offset-assert 168)
|
||||
std::vector<RaceMesh> race_meshes;
|
||||
|
||||
// (actor-birth-order (pointer uint32) :offset-assert 172)
|
||||
std::vector<u32> actor_birth_order;
|
||||
|
||||
// (light-hash light-hash :offset-assert 176)
|
||||
LightHash light_hash;
|
||||
// (nav-meshes (array entity-nav-mesh) :offset-assert 180)
|
||||
std::vector<EntityNavMesh> entity_nav_meshes;
|
||||
// (actor-groups (array actor-group) :offset-assert 184)
|
||||
std::vector<ActorGroup> actor_groups;
|
||||
// (region-trees (array drawable-tree-region-prim) :offset-assert 188)
|
||||
std::vector<RegionTree> region_trees;
|
||||
// (region-array region-array :offset-assert 192)
|
||||
RegionArray region_array;
|
||||
// (collide-hash collide-hash :offset-assert 196)
|
||||
CollideHash collide_hash;
|
||||
// (wind-array uint32 :offset 200)
|
||||
std::vector<u32> wind_array;
|
||||
// (wind-array-length int32 :offset 204)
|
||||
s32 wind_array_length = 0;
|
||||
// (city-level-info city-level-info :offset 208)
|
||||
CityLevelInfo city_level_info;
|
||||
// (vis-spheres vector-array :offset 216)
|
||||
// (vis-spheres-length uint32 :offset 248)
|
||||
|
||||
// (region-tree drawable-tree-region-prim :offset 252)
|
||||
RegionTree region_tree;
|
||||
// (tfrag-masks texture-masks-array :offset-assert 256)
|
||||
// (tfrag-closest (pointer float) :offset-assert 260)
|
||||
// (tfrag-mask-count uint32 :offset 260)
|
||||
TextureMasksArray tfrag_masks;
|
||||
// (shrub-masks texture-masks-array :offset-assert 264)
|
||||
// (shrub-closest (pointer float) :offset-assert 268)
|
||||
// (shrub-mask-count uint32 :offset 268)
|
||||
TextureMasksArray shrub_masks;
|
||||
// (alpha-masks texture-masks-array :offset-assert 272)
|
||||
// (alpha-closest (pointer float) :offset-assert 276)
|
||||
// (alpha-mask-count uint32 :offset 276)
|
||||
TextureMasksArray alpha_masks;
|
||||
// (water-masks texture-masks-array :offset-assert 280)
|
||||
// (water-closest (pointer float) :offset-assert 284)
|
||||
// (water-mask-count uint32 :offset 284)
|
||||
TextureMasksArray water_masks;
|
||||
// (bsp-scale vector :inline :offset-assert 288)
|
||||
// (bsp-offset vector :inline :offset-assert 304)
|
||||
|
||||
std::vector<u8> save_object_file() const;
|
||||
};
|
||||
} // namespace jak2
|
||||
@@ -0,0 +1,202 @@
|
||||
#include "build_level.h"
|
||||
|
||||
#include "decompiler/extractor/extractor_util.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"
|
||||
#include "goalc/build_level/common/Tfrag.h"
|
||||
#include "goalc/build_level/jak2/Entity.h"
|
||||
#include "goalc/build_level/jak2/FileInfo.h"
|
||||
#include "goalc/build_level/jak2/LevelFile.h"
|
||||
|
||||
namespace jak2 {
|
||||
bool run_build_level(const std::string& input_file,
|
||||
const std::string& bsp_output_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
|
||||
tfrag3::Level pc_level; // PC level file
|
||||
TexturePool tex_pool; // pc level texture pool
|
||||
|
||||
// process input mesh from blender
|
||||
gltf_mesh_extract::Input mesh_extract_in;
|
||||
mesh_extract_in.filename =
|
||||
file_util::get_file_path({level_json.at("gltf_file").get<std::string>()});
|
||||
mesh_extract_in.auto_wall_enable = level_json.value("automatic_wall_detection", true);
|
||||
mesh_extract_in.double_sided_collide = level_json.at("double_sided_collide").get<bool>();
|
||||
mesh_extract_in.auto_wall_angle = level_json.value("automatic_wall_angle", 30.0);
|
||||
mesh_extract_in.tex_pool = &tex_pool;
|
||||
gltf_mesh_extract::Output mesh_extract_out;
|
||||
gltf_mesh_extract::extract(mesh_extract_in, mesh_extract_out);
|
||||
|
||||
// add stuff to the GOAL level structure
|
||||
file.info = make_file_info_for_level(fs::path(input_file).filename().string());
|
||||
// all vis
|
||||
// drawable trees
|
||||
// pat
|
||||
// texture remap
|
||||
// texture ids
|
||||
// unk zero
|
||||
// name
|
||||
file.name = level_json.at("long_name").get<std::string>();
|
||||
// nick
|
||||
file.nickname = level_json.at("nickname").get<std::string>();
|
||||
// vis infos
|
||||
// actors
|
||||
std::vector<EntityActor> actors;
|
||||
add_actors_from_json(level_json.at("actors"), actors, level_json.value("base_id", 1234));
|
||||
file.actors = std::move(actors);
|
||||
// cameras
|
||||
// nodes
|
||||
// regions
|
||||
// subdivs
|
||||
// actor birth
|
||||
for (size_t i = 0; i < file.actors.size(); i++) {
|
||||
file.actor_birth_order.push_back(i);
|
||||
}
|
||||
|
||||
// add stuff to the PC level structure
|
||||
pc_level.level_name = file.name;
|
||||
|
||||
// TFRAG
|
||||
auto& tfrag_drawable_tree = file.drawable_trees.tfrags.emplace_back();
|
||||
tfrag_from_gltf(mesh_extract_out.tfrag, tfrag_drawable_tree,
|
||||
pc_level.tfrag_trees[0].emplace_back());
|
||||
pc_level.textures = std::move(tex_pool.textures_by_idx);
|
||||
|
||||
// COLLIDE
|
||||
if (mesh_extract_out.collide.faces.empty()) {
|
||||
lg::error("No collision geometry was found");
|
||||
} else {
|
||||
auto& collide_drawable_tree = file.drawable_trees.collides.emplace_back();
|
||||
collide_drawable_tree.bvh = collide::construct_collide_bvh(mesh_extract_out.collide.faces);
|
||||
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());
|
||||
|
||||
// Add textures and models
|
||||
// TODO remove hardcoded config settings
|
||||
if ((level_json.contains("art_groups") && !level_json.at("art_groups").empty()) ||
|
||||
(level_json.contains("textures") && !level_json.at("textures").empty())) {
|
||||
fs::path iso_folder = "";
|
||||
lg::info("Looking for ISO path...");
|
||||
// TODO - add to file_util
|
||||
for (const auto& entry :
|
||||
fs::directory_iterator(file_util::get_jak_project_dir() / "iso_data")) {
|
||||
// TODO - hard-coded to jak 2
|
||||
if (entry.is_directory() &&
|
||||
entry.path().filename().string().find("jak2") != std::string::npos) {
|
||||
lg::info("Found ISO path: {}", entry.path().string());
|
||||
iso_folder = entry.path();
|
||||
}
|
||||
}
|
||||
|
||||
if (iso_folder.empty() || !fs::exists(iso_folder)) {
|
||||
lg::warn("Could not locate ISO path!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Look for iso build info if it's available, otherwise default to ntsc_v1
|
||||
const auto version_info = get_version_info_or_default(iso_folder);
|
||||
|
||||
decompiler::Config config;
|
||||
try {
|
||||
config = decompiler::read_config_file(
|
||||
file_util::get_jak_project_dir() / "decompiler/config/jak2/jak2_config.jsonc",
|
||||
version_info.decomp_config_version,
|
||||
R"({"decompile_code": false, "find_functions": false, "levels_extract": true, "allowed_objects": [], "save_texture_pngs": false})");
|
||||
} catch (const std::exception& e) {
|
||||
lg::error("Failed to parse config: {}", e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<fs::path> dgos, objs;
|
||||
for (const auto& dgo_name : config.dgo_names) {
|
||||
dgos.push_back(iso_folder / dgo_name);
|
||||
}
|
||||
|
||||
for (const auto& obj_name : config.object_file_names) {
|
||||
objs.push_back(iso_folder / obj_name);
|
||||
}
|
||||
|
||||
decompiler::ObjectFileDB db(dgos, fs::path(config.obj_file_name_map_file), objs, {}, {},
|
||||
config);
|
||||
|
||||
// need to process link data for tpages
|
||||
db.process_link_data(config);
|
||||
|
||||
decompiler::TextureDB tex_db;
|
||||
auto textures_out = file_util::get_jak_project_dir() / "decompiler_out/jak2/textures";
|
||||
file_util::create_dir_if_needed(textures_out);
|
||||
db.process_tpages(tex_db, textures_out, config);
|
||||
|
||||
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()) {
|
||||
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);
|
||||
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);
|
||||
decompiler::extract_merc(ag_file, tex_db, db.dts, tex_remap, pc_level, false,
|
||||
db.version());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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>>();
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save the PC level
|
||||
save_pc_data(file.nickname, pc_level,
|
||||
file_util::get_jak_project_dir() / "out" / output_prefix / "fr3");
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace jak2
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "goalc/build_level/common/build_level.h"
|
||||
|
||||
namespace jak2 {
|
||||
bool run_build_level(const std::string& input_file,
|
||||
const std::string& bsp_output_file,
|
||||
const std::string& output_prefix);
|
||||
} // namespace jak2
|
||||
Reference in New Issue
Block a user