mirror of
https://github.com/open-goal/jak-project
synced 2026-08-22 07:04:29 -04:00
custom levels: refactor level building code and jak 2 support (#3090)
Co-authored-by: water <awaterford111445@gmail.com>
This commit is contained in:
@@ -18,6 +18,12 @@ class Vector {
|
||||
return result;
|
||||
}
|
||||
|
||||
static Vector<T, Size> unit(int idx) {
|
||||
Vector<T, Size> result = Vector<T, Size>::zero();
|
||||
result[idx] = T(1);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
constexpr Vector(Args... args) : m_data{T(args)...} {
|
||||
static_assert(sizeof...(args) == Size, "Incorrect number of args");
|
||||
@@ -226,6 +232,22 @@ class Vector {
|
||||
}
|
||||
}
|
||||
|
||||
Vector<T, Size> min(const Vector<T, Size>& other) const {
|
||||
Vector<T, Size> result;
|
||||
for (int i = 0; i < Size; i++) {
|
||||
result[i] = std::min(m_data[i], other[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector<T, Size> max(const Vector<T, Size>& other) const {
|
||||
Vector<T, Size> result;
|
||||
for (int i = 0; i < Size; i++) {
|
||||
result[i] = std::max(m_data[i], other[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string to_string_aligned() const {
|
||||
std::string result = "[";
|
||||
for (auto x : m_data) {
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
// Must have vertex colors. Use the blender cycles renderer, bake, diffuse, uncheck color,
|
||||
// and bake to vertex colors. For now, only the first vertex color group is used, so make sure you
|
||||
// only have 1.
|
||||
"gltf_file": "custom_levels/test-zone/test-zone2.glb",
|
||||
"gltf_file": "custom_levels/jak1/test-zone/test-zone2.glb",
|
||||
|
||||
// automatically set wall vs. ground based on angle. Useful if you don't want to assign this yourself
|
||||
"automatic_wall_detection": true,
|
||||
@@ -0,0 +1,83 @@
|
||||
{
|
||||
// The "in-game" name of the level. Should be lower case, with dashes (GOAL symbol name)
|
||||
// the name of this file, and the folder this file is in must have the same name.
|
||||
"long_name": "test-zone",
|
||||
// The file name, should be upper case and 8 characters or less.
|
||||
"iso_name": "TESTZONE",
|
||||
// The nickname, should be exactly 3 characters
|
||||
"nickname": "tsz", // 3 char name, all lowercase
|
||||
// Background mesh file.
|
||||
// Must have vertex colors. Use the blender cycles renderer, bake, diffuse, uncheck color,
|
||||
// and bake to vertex colors. For now, only the first vertex color group is used, so make sure you
|
||||
// only have 1.
|
||||
"gltf_file": "custom_levels/jak2/test-zone/test-zone2.glb",
|
||||
|
||||
// automatically set wall vs. ground based on angle. Useful if you don't want to assign this yourself
|
||||
"automatic_wall_detection": true,
|
||||
"automatic_wall_angle": 45.0,
|
||||
|
||||
// if your mesh has triangles with incorrect orientation, set this to make all collision mesh triangles double sided
|
||||
// this makes collision 2x slower and bigger, so only use if really needed
|
||||
"double_sided_collide": false,
|
||||
|
||||
// available res-lump tag data types:
|
||||
// int32, float, meters, vector, vector4m (meters)
|
||||
//
|
||||
// examples:
|
||||
//
|
||||
// adds a float tag 'spring-height' with value of 200 meters (1 meter = 4096.0 units):
|
||||
// "spring-height": ["meters", 200.0]
|
||||
//
|
||||
// adds a vector tag 'movie-pos':
|
||||
// "movie-pos": ["vector", [4096000.0, -176128.0, 1353973.76, 1.0]]
|
||||
|
||||
// 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": ["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": [],
|
||||
|
||||
"actors" : [
|
||||
{
|
||||
"trans": [-15.2818, 15.2461, 17.1360], // 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
|
||||
"lump": {
|
||||
"name": "test-crate",
|
||||
"eco-info": ["int32", 18, 2]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"trans": [-5.4630, 17.4553, 1.6169], // translation
|
||||
"etype": "eco-yellow", // actor type
|
||||
"game_task": 0, // 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
|
||||
"lump": {
|
||||
"name": "test-eco"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"trans": [-7.41, 13.5, 28.42], // translation
|
||||
"etype": "prsn-torture", // actor type
|
||||
"game_task": 0, // associated game task (for powercells, etc)
|
||||
"quat": [0, 0, 0, 1], // quaternion
|
||||
"bsphere": [-7.41, 13.5, 28.42, 10], // bounding sphere
|
||||
"lump": {
|
||||
"name": "test-torture"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,9 @@
|
||||
;; DGO definition file for Awful Village level
|
||||
;; We use the convention of having a longer DGO name for levels without precomputed visibility.
|
||||
|
||||
;; the actual file name still needs to be 8.3
|
||||
("TSZ.DGO"
|
||||
(
|
||||
"prison-obs.o"
|
||||
"test-zone.go"
|
||||
))
|
||||
@@ -154,9 +154,9 @@
|
||||
)
|
||||
|
||||
(defun custom-level-cgo (output-name desc-file-name)
|
||||
"Add a CGO with the given output name (in $OUT/iso) and input name (in custom_levels/)"
|
||||
"Add a CGO with the given output name (in $OUT/iso) and input name (in custom_levels/jak1/)"
|
||||
(let ((out-name (string-append "$OUT/iso/" output-name)))
|
||||
(defstep :in (string-append "custom_levels/" desc-file-name)
|
||||
(defstep :in (string-append "custom_levels/jak1/" desc-file-name)
|
||||
:tool 'dgo
|
||||
:out `(,out-name)
|
||||
)
|
||||
@@ -208,7 +208,7 @@
|
||||
)
|
||||
|
||||
(defmacro build-custom-level (name)
|
||||
(let* ((path (string-append "custom_levels/" name "/" name ".jsonc")))
|
||||
(let* ((path (string-append "custom_levels/jak1/" name "/" name ".jsonc")))
|
||||
`(defstep :in ,path
|
||||
:tool 'build-level
|
||||
:out '(,(string-append "$OUT/obj/" name ".go")))))
|
||||
@@ -1636,7 +1636,7 @@
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; Set up the build system to build the level geometry
|
||||
;; this path is relative to the custom_levels/ folder
|
||||
;; this path is relative to the custom_levels/jak1 folder
|
||||
;; it should point to the .jsonc file that specifies the level.
|
||||
(build-custom-level "test-zone")
|
||||
;; the DGO file
|
||||
|
||||
@@ -14690,7 +14690,7 @@
|
||||
0
|
||||
)
|
||||
|
||||
;; added in PC port: test levels from the ps3 version
|
||||
;; og:preserve-this added in PC port: test levels from the ps3 version
|
||||
(#when USE_PS3_LEVELS
|
||||
(define 4aaron (new 'static 'level-load-info
|
||||
:index 1
|
||||
@@ -15724,3 +15724,88 @@
|
||||
(cons! *level-load-list* 'wasall)
|
||||
(cons! *level-load-list* 'stadocc)
|
||||
)
|
||||
|
||||
;; og:preserve-this added test-zone level
|
||||
(define test-zone
|
||||
(new 'static 'level-load-info
|
||||
:index #x9e
|
||||
:name 'test-zone
|
||||
:visname 'test-zone-vis
|
||||
:nickname 'tsz
|
||||
:dbname 'test-zone
|
||||
:taskname 'default
|
||||
:packages '()
|
||||
;; :memory-mode (load-buffer-mode small-center)
|
||||
:music-bank #f
|
||||
:ambient-sounds '()
|
||||
:mood-func 'update-mood-default
|
||||
:mood-init #f
|
||||
:ocean #f
|
||||
:sky #t
|
||||
:use-camera-other #f
|
||||
:part-engine-max 16
|
||||
:continues '((new 'static 'continue-point
|
||||
:name "test-zone-start"
|
||||
:level 'test-zone
|
||||
:trans (new 'static 'vector :x 0.0 :y (meters 10) :z (meters 10) :w 1.0)
|
||||
:quat (new 'static 'vector :y 0.061 :w 0.9981)
|
||||
:camera-trans (new 'static 'vector :x 0.0 :y (meters 1) :z 0.0 :w 1.0)
|
||||
:camera-rot (new 'static 'inline-array vector3s 3
|
||||
(new 'static 'vector3s :data (new 'static 'array float 3 1.0 0.0 0.0))
|
||||
(new 'static 'vector3s :data (new 'static 'array float 3 0.0 1.0 0.0))
|
||||
(new 'static 'vector3s :data (new 'static 'array float 3 0.0 0.0 1.0))
|
||||
)
|
||||
:on-goto #f
|
||||
:vis-nick #f
|
||||
:want (new 'static 'inline-array level-buffer-state 6
|
||||
(new 'static 'level-buffer-state :name 'test-zone :display? 'display :force-vis? #f :force-inside? #f)
|
||||
(new 'static 'level-buffer-state :name 'ctywide :display? 'display :force-vis? #f :force-inside? #f)
|
||||
(new 'static 'level-buffer-state :name #f :display? #f :force-vis? #f :force-inside? #f)
|
||||
(new 'static 'level-buffer-state :name #f :display? #f :force-vis? #f :force-inside? #f)
|
||||
(new 'static 'level-buffer-state :name #f :display? #f :force-vis? #f :force-inside? #f)
|
||||
(new 'static 'level-buffer-state :name #f :display? #f :force-vis? #f :force-inside? #f)
|
||||
)
|
||||
:want-sound (new 'static 'array symbol 3 #f #f #f)
|
||||
)
|
||||
)
|
||||
:tasks '()
|
||||
:priority 100
|
||||
:load-commands '()
|
||||
:alt-load-commands '()
|
||||
:bsp-mask #xffffffffffffffff
|
||||
:buttom-height (meters -10000000)
|
||||
:run-packages '()
|
||||
:wait-for-load #t
|
||||
:login-func #f
|
||||
:activate-func #f
|
||||
:deactivate-func #f
|
||||
:kill-func #f
|
||||
:borrow-level (new 'static 'array symbol 2 #f #f)
|
||||
:borrow-display? (new 'static 'array symbol 2 #f #f)
|
||||
:base-task-mask (task-mask task0)
|
||||
:texture-anim-tfrag #f
|
||||
:texture-anim-pris #f
|
||||
:texture-anim-shrub #f
|
||||
:texture-anim-alpha #f
|
||||
:texture-anim-water #f
|
||||
:texture-anim-twarp #f
|
||||
:texture-anim-pris2 #f
|
||||
:texture-anim-sprite #f
|
||||
:texture-anim-map #f
|
||||
:texture-anim-sky #f
|
||||
:draw-priority 10.0
|
||||
:fog-height 327680.0
|
||||
:bigmap-id (bigmap-id bigmap-id-20)
|
||||
:ocean-near-translucent? #t
|
||||
:ocean-far? #t
|
||||
:mood-range (new 'static 'mood-range :data (new 'static 'array float 4 0.0 1.0 0.0 1.0))
|
||||
:max-rain 1.0
|
||||
:fog-mult 1.0
|
||||
:ocean-alpha 1.0
|
||||
:extra-sound-bank #f
|
||||
)
|
||||
)
|
||||
|
||||
(#when PC_PORT
|
||||
(cons! *level-load-list* 'test-zone)
|
||||
)
|
||||
|
||||
@@ -262,7 +262,8 @@
|
||||
)
|
||||
)
|
||||
(let ((s5-4 (level-get *level* (-> arg0 level))))
|
||||
(when s5-4
|
||||
;; og:preserve-this don't wait for vis if level doesn't have it
|
||||
(when (and s5-4 (-> s5-4 vis-info 0))
|
||||
(while (and (-> *level* vis?) (-> s5-4 vis-info 0) (= (-> s5-4 all-visible?) 'loading))
|
||||
(suspend)
|
||||
)
|
||||
|
||||
@@ -291,6 +291,17 @@
|
||||
(cgo-file "wasall.gd" common-dep)
|
||||
)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Example Custom Level
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; Set up the build system to build the level geometry
|
||||
;; this path is relative to the custom_levels/jak2 folder
|
||||
;; it should point to the .jsonc file that specifies the level.
|
||||
(build-custom-level "test-zone")
|
||||
;; the DGO file
|
||||
(custom-level-cgo "TSZ.DGO" "test-zone/testzone.gd")
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;
|
||||
;; ANIMATIONS
|
||||
;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
@@ -83,6 +83,17 @@
|
||||
)
|
||||
)
|
||||
|
||||
(defun custom-level-cgo (output-name desc-file-name)
|
||||
"Add a CGO with the given output name (in $OUT/iso) and input name (in custom_levels/jak2/)"
|
||||
(let ((out-name (string-append "$OUT/iso/" output-name)))
|
||||
(defstep :in (string-append "custom_levels/jak2/" desc-file-name)
|
||||
:tool 'dgo
|
||||
:out `(,out-name)
|
||||
)
|
||||
(set! *all-cgos* (cons out-name *all-cgos*))
|
||||
)
|
||||
)
|
||||
|
||||
(defun cgo (output-name desc-file-name)
|
||||
"Add a CGO with the given output name (in $OUT/iso) and input name (in goal_src/jak2/dgos)"
|
||||
(let ((out-name (string-append "$OUT/iso/" output-name)))
|
||||
@@ -124,6 +135,12 @@
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro build-custom-level (name)
|
||||
(let* ((path (string-append "custom_levels/jak2/" name "/" name ".jsonc")))
|
||||
`(defstep :in ,path
|
||||
:tool 'build-level2
|
||||
:out '(,(string-append "$OUT/obj/" name ".go")))))
|
||||
|
||||
(defmacro group (name &rest stuff)
|
||||
`(defstep :in ""
|
||||
:tool 'group
|
||||
|
||||
+23
-12
@@ -5,18 +5,26 @@ 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/collide/jak2/collide.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
|
||||
@@ -65,6 +73,9 @@ endif ()
|
||||
add_executable(goalc main.cpp)
|
||||
add_executable(goalc-simple simple_main.cpp)
|
||||
|
||||
add_executable(build_level build_level/main.cpp)
|
||||
|
||||
target_link_libraries(goalc common Zydis compiler)
|
||||
target_link_libraries(goalc-simple common Zydis compiler)
|
||||
target_link_libraries(build_level common Zydis compiler)
|
||||
|
||||
|
||||
@@ -0,0 +1,314 @@
|
||||
#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::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);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/math/Vector.h"
|
||||
|
||||
#include "goalc/build_level/collide/common/collide_common.h"
|
||||
|
||||
// High-level collision system idea:
|
||||
// Each level has a single collide-hash object storing all collision data.
|
||||
// The mesh is divided into "fragments". Each fragment is made up of triangles.
|
||||
// There's a two-level lookup: if you want to find all triangles in a box, you must first find all
|
||||
// the fragments that intersect the box, then find all the triangles in those fragments that
|
||||
// intersect the box.
|
||||
// Each fragment has a bounding box. All triangles inside that fragment fit inside the bounding box.
|
||||
|
||||
/*!
|
||||
* Vertex in the collide mesh. This is stored as an offset from the bottom corner of the bounding
|
||||
* box. This is scaled by 16. (a "1" stored here means a distance of 16.f, or 16/4096 of in-game
|
||||
* meter.)
|
||||
*/
|
||||
struct CollideFragmentVertex {
|
||||
u16 position[3];
|
||||
};
|
||||
|
||||
/*!
|
||||
* Polygon in the collide mesh. This is a reference to three vertices in the vertex array, and a
|
||||
* "pat" (polygon attributes?) in the pat array.
|
||||
*/
|
||||
struct CollideFragmentPoly {
|
||||
u8 vertex_index[3];
|
||||
u8 pat_index;
|
||||
};
|
||||
|
||||
/*!
|
||||
* The Collide Fragment is divided into a 3D grid. Each cell in the grid has a "bucket" which
|
||||
* collects a list of all polygons that intersect the cell. The bucket stores a reference to values
|
||||
* in the index list, which are polygon indices.
|
||||
*/
|
||||
struct CollideBucket {
|
||||
s16 index;
|
||||
s16 count;
|
||||
};
|
||||
|
||||
struct CollideFragment {
|
||||
std::vector<jak2::PatSurface> pat_array;
|
||||
|
||||
// per-cell references to the index list
|
||||
std::vector<CollideBucket> buckets;
|
||||
|
||||
// references to polygons
|
||||
std::vector<u8> index_array;
|
||||
|
||||
// references to vertices/pats
|
||||
std::vector<CollideFragmentPoly> poly_array;
|
||||
|
||||
std::vector<CollideFragmentVertex> vert_array;
|
||||
|
||||
// others
|
||||
|
||||
// the x/y/z sizes of a grid cell
|
||||
math::Vector3f grid_step;
|
||||
|
||||
// inverse of grid step
|
||||
math::Vector3f axis_scale;
|
||||
|
||||
// the corners of our bounding box
|
||||
math::Vector3f bbox_min_corner;
|
||||
math::Vector3f bbox_max_corner;
|
||||
math::Vector4f bsphere;
|
||||
math::Vector<s32, 3> bbox_min_corner_i;
|
||||
math::Vector<s32, 3> bbox_max_corner_i;
|
||||
|
||||
// the number of cells in the grid along the x/y/z axis
|
||||
u32 dimension_array[3] = {0, 0, 0};
|
||||
};
|
||||
|
||||
/*
|
||||
((num-ids uint16 :offset 4)
|
||||
(id-count uint16 :offset 6)
|
||||
(num-buckets uint32 :offset 8)
|
||||
(qwc-id-bits uint32 :offset 12)
|
||||
(grid-step vector :inline :offset 16)
|
||||
(bbox bounding-box :inline :offset-assert 32)
|
||||
(bbox4w bounding-box4w :inline :offset-assert 64)
|
||||
(axis-scale vector :inline :offset 48)
|
||||
(avg-extents vector :inline :offset 64)
|
||||
(bucket-array uint32 :offset 44)
|
||||
(item-array (inline-array collide-hash-item) :offset 60 :score 1)
|
||||
(dimension-array uint32 3 :offset 76) ;; ?
|
||||
(num-items uint32 :offset 92)
|
||||
*/
|
||||
|
||||
struct CollideHash {
|
||||
// if you have a bit for each ID in the item list, how many quadwords (128-byte word) is it?
|
||||
u32 qwc_id_bits = 0;
|
||||
|
||||
// this is similar to the use in CollideHashFragment, but this points to entries in the .
|
||||
std::vector<CollideBucket> buckets;
|
||||
|
||||
// buckets point to this array, which points to the fragments below
|
||||
std::vector<u32> index_array;
|
||||
|
||||
// the actual fragments
|
||||
std::vector<CollideFragment> fragments;
|
||||
|
||||
// all these have the same meaning as in CollideFragment and define the grid.
|
||||
math::Vector3f grid_step;
|
||||
math::Vector3f axis_scale;
|
||||
math::Vector3f bbox_min_corner;
|
||||
math::Vector<s32, 3> bbox_min_corner_i;
|
||||
math::Vector<s32, 3> bbox_max_corner_i;
|
||||
u32 dimension_array[3] = {0, 0, 0};
|
||||
};
|
||||
|
||||
CollideHash construct_collide_hash(const std::vector<jak1::CollideFace>& tris);
|
||||
CollideHash construct_collide_hash(const std::vector<jak2::CollideFace>& tris);
|
||||
|
||||
class DataObjectGenerator;
|
||||
|
||||
size_t add_to_object_file(const CollideHash& hash, DataObjectGenerator& gen);
|
||||
@@ -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,140 @@
|
||||
#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();
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
gen.link_word_to_byte(196 / 4, add_to_object_file(collide_hash, gen));
|
||||
//(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,182 @@
|
||||
#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/jak2/collide.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<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 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,198 @@
|
||||
#include "build_level.h"
|
||||
|
||||
#include "decompiler/extractor/extractor_util.h"
|
||||
#include "decompiler/level_extractor/extract_merc.h"
|
||||
#include "goalc/build_level/collide/jak2/collide.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 {
|
||||
file.collide_hash = construct_collide_hash(mesh_extract_out.collide.faces);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// 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) {
|
||||
std::vector<std::string> processed_art_groups;
|
||||
// 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
|
||||
@@ -0,0 +1,69 @@
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/Assert.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/versions/versions.h"
|
||||
|
||||
#include "goalc/build_level/jak1/build_level.h"
|
||||
#include "goalc/build_level/jak2/build_level.h"
|
||||
|
||||
#include "third-party/CLI11.hpp"
|
||||
|
||||
// debug tool to run only build_level.
|
||||
int main(int argc, char** argv) {
|
||||
// logging
|
||||
lg::set_stdout_level(lg::level::info);
|
||||
lg::set_flush_level(lg::level::info);
|
||||
lg::initialize();
|
||||
|
||||
// game version
|
||||
std::string game, input_json, output_file;
|
||||
fs::path project_path_override;
|
||||
|
||||
// path
|
||||
if (!file_util::setup_project_path(std::nullopt)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
lg::info("Build Level Tool", versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR);
|
||||
|
||||
CLI::App app{"OpenGOAL Compiler / REPL"};
|
||||
app.add_option("input-json", input_json,
|
||||
"Input JSON file (for example, custom_levels/jak2/test-zone/test-zone.jsonc)")
|
||||
->required();
|
||||
app.add_option("output-file", output_file,
|
||||
"Output .go file, (for example out/jak2/obj/test-zone.go)")
|
||||
->required();
|
||||
app.add_option("-g,--game", game, "Game version (jak1 or jak2)")->required();
|
||||
app.add_option("--proj-path", project_path_override,
|
||||
"Specify the location of the 'data/' folder");
|
||||
app.validate_positionals();
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
GameVersion game_version = game_name_to_version(game);
|
||||
|
||||
if (!project_path_override.empty()) {
|
||||
if (!fs::exists(project_path_override)) {
|
||||
lg::error("Error: project path override '{}' does not exist", project_path_override.string());
|
||||
return 1;
|
||||
}
|
||||
if (!file_util::setup_project_path(project_path_override)) {
|
||||
lg::error("Could not setup project path!");
|
||||
return 1;
|
||||
}
|
||||
} else if (!file_util::setup_project_path(std::nullopt)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (game_version) {
|
||||
case GameVersion::Jak1:
|
||||
jak1::run_build_level(input_json, output_file, "jak1/");
|
||||
break;
|
||||
case GameVersion::Jak2:
|
||||
jak2::run_build_level(input_json, output_file, "jak2/");
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED_MSG("unsupported game version");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -102,6 +102,7 @@ MakeSystem::MakeSystem(const std::optional<REPL::Config> repl_config, const std:
|
||||
add_tool<SubtitleTool>();
|
||||
add_tool<SubtitleV2Tool>();
|
||||
add_tool<BuildLevelTool>();
|
||||
add_tool<BuildLevel2Tool>();
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
+20
-2
@@ -4,7 +4,8 @@
|
||||
#include "common/util/DgoWriter.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
#include "goalc/build_level/build_level.h"
|
||||
#include "goalc/build_level/jak1/build_level.h"
|
||||
#include "goalc/build_level/jak2/build_level.h"
|
||||
#include "goalc/compiler/Compiler.h"
|
||||
#include "goalc/data_compiler/dir_tpages.h"
|
||||
#include "goalc/data_compiler/game_count.h"
|
||||
@@ -258,5 +259,22 @@ bool BuildLevelTool::run(const ToolInput& task, const PathMap& path_map) {
|
||||
if (task.input.size() != 1) {
|
||||
throw std::runtime_error(fmt::format("Invalid amount of inputs to {} tool", name()));
|
||||
}
|
||||
return run_build_level(task.input.at(0), task.output.at(0), path_map.output_prefix);
|
||||
return jak1::run_build_level(task.input.at(0), task.output.at(0), path_map.output_prefix);
|
||||
}
|
||||
|
||||
BuildLevel2Tool::BuildLevel2Tool() : Tool("build-level2") {}
|
||||
|
||||
bool BuildLevel2Tool::needs_run(const ToolInput& task, const PathMap& path_map) {
|
||||
if (task.input.size() != 1) {
|
||||
throw std::runtime_error(fmt::format("Invalid amount of inputs to {} tool", name()));
|
||||
}
|
||||
auto deps = get_build_level_deps(task.input.at(0));
|
||||
return Tool::needs_run({task.input, deps, task.output, task.arg}, path_map);
|
||||
}
|
||||
|
||||
bool BuildLevel2Tool::run(const ToolInput& task, const PathMap& path_map) {
|
||||
if (task.input.size() != 1) {
|
||||
throw std::runtime_error(fmt::format("Invalid amount of inputs to {} tool", name()));
|
||||
}
|
||||
return jak2::run_build_level(task.input.at(0), task.output.at(0), path_map.output_prefix);
|
||||
}
|
||||
|
||||
@@ -78,3 +78,10 @@ class BuildLevelTool : public Tool {
|
||||
bool run(const ToolInput& task, const PathMap& path_map) override;
|
||||
bool needs_run(const ToolInput& task, const PathMap& path_map) override;
|
||||
};
|
||||
|
||||
class BuildLevel2Tool : public Tool {
|
||||
public:
|
||||
BuildLevel2Tool();
|
||||
bool run(const ToolInput& task, const PathMap& path_map) override;
|
||||
bool needs_run(const ToolInput& task, const PathMap& path_map) override;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user