mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 00:57:44 -04:00
[build_level] basic support for entity/res-lump (#1563)
* [build_level] drawable actor stuff * clean up * improved bsphere * windows include nonsense * final tuning * m_pi * what * going insane * merge conflict fix * windows
This commit is contained in:
parent
fa3f4b0a88
commit
54937a95b1
|
@ -11,5 +11,43 @@
|
||||||
// Must have vertex colors. Use the blender cycles renderer, bake, diffuse, uncheck color,
|
// 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
|
// and bake to vertex colors. For now, only the first vertex color group is used, so make sure you
|
||||||
// only have 1.
|
// only have 1.
|
||||||
"gltf_file": "custom_levels/test-zone/test-zone2.glb"
|
"gltf_file": "custom_levels/test-zone/test-zone2.glb",
|
||||||
|
"automatic_wall_detection": true,
|
||||||
|
"automatic_wall_angle": 45.0,
|
||||||
|
"actors" : [
|
||||||
|
{
|
||||||
|
"trans": [-21.6238, 20.0496, 17.1191], // translation
|
||||||
|
"etype": "fuel-cell", // actor type
|
||||||
|
"game_task": 0, // associated game task (for powercells, etc)
|
||||||
|
"quat" : [0, 0, 0, 1], // quaternion
|
||||||
|
"bsphere": [-21.6238, 19.3496, 17.1191, 10], // bounding sphere
|
||||||
|
"lump": {
|
||||||
|
"name":"test-fuel-cell"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"trans": [-15.2818, 15.2461, 17.1360], // translation
|
||||||
|
"etype": "crate", // actor type
|
||||||
|
"game_task": 0, // associated game task (for powercells, etc)
|
||||||
|
"quat" : [0, 0, 0, 1], // quaternion
|
||||||
|
"bsphere": [-15.2818, 15.2461, 17.1360, 10], // bounding sphere
|
||||||
|
"lump": {
|
||||||
|
"name":"test-crate",
|
||||||
|
"crate-type":"'steel",
|
||||||
|
"eco-info": ["int32", 5, 10]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"trans": [-5.4630, 17.4553, 1.6169], // translation
|
||||||
|
"etype": "eco-yellow", // actor type
|
||||||
|
"game_task": 0, // associated game task (for powercells, etc)
|
||||||
|
"quat" : [0, 0, 0, 1], // quaternion
|
||||||
|
"bsphere": [-5.4630, 17.4553, 1.6169, 10], // bounding sphere
|
||||||
|
"lump": {
|
||||||
|
"name":"test-eco"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
|
@ -4664,3 +4664,69 @@
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
(defun bg-custom ((level-name symbol))
|
||||||
|
"Modified version of bg for the PC Port custom levels."
|
||||||
|
|
||||||
|
;; lookup info
|
||||||
|
(format 0 "(bg-custom ~A)%" level-name)
|
||||||
|
(let ((lev-info (lookup-level-info level-name)))
|
||||||
|
(when (= lev-info default-level)
|
||||||
|
(format 0 "Unable to (bg-custom ~A), the level was not found in *level-load-list*~%" level-name)
|
||||||
|
(return #f)
|
||||||
|
)
|
||||||
|
|
||||||
|
;; kill jak (rip)
|
||||||
|
(format 0 "doing stop~%")
|
||||||
|
(stop 'play)
|
||||||
|
|
||||||
|
;; kill levels
|
||||||
|
(dotimes (i 2)
|
||||||
|
(unload! (-> *level* data i))
|
||||||
|
)
|
||||||
|
|
||||||
|
;; enable visiblity. the custom level won't use it, but we want it on so other levels can be loaded.
|
||||||
|
(set! (-> *level* vis?) #t)
|
||||||
|
|
||||||
|
;; disable border and play mode to prevent loading levels
|
||||||
|
(set! (-> *level* border?) #f)
|
||||||
|
(set! (-> *setting-control* default border-mode) #f)
|
||||||
|
(set! (-> *level* play?) #f)
|
||||||
|
|
||||||
|
;; disable actor vis
|
||||||
|
(set! *vis-actors* #f)
|
||||||
|
|
||||||
|
(format 0 "doing level load~%")
|
||||||
|
;; allocate level. This may start the loading process, but won't finish it.
|
||||||
|
(let ((lev (level-get-for-use *level* level-name 'active)))
|
||||||
|
(when (not lev)
|
||||||
|
(format 0 "Unable to load level, could not level-get-for-use~%")
|
||||||
|
(return #f)
|
||||||
|
)
|
||||||
|
(format 0 "about to start load loop, game will freeze and hopefully come back soon~%")
|
||||||
|
|
||||||
|
;; spin in a loop and load it. This will cause the game to freeze during the load,
|
||||||
|
;; but this is good enough for now.
|
||||||
|
(while (or (= (-> lev status) 'loading)
|
||||||
|
(= (-> lev status) 'loading-bt)
|
||||||
|
(= (-> lev status) 'login)
|
||||||
|
)
|
||||||
|
(load-continue lev)
|
||||||
|
)
|
||||||
|
|
||||||
|
(when (not (-> lev info continues))
|
||||||
|
(format 0 "level info has no continues, can't load it.~%")
|
||||||
|
)
|
||||||
|
|
||||||
|
(let ((cont (car (-> lev info continues))))
|
||||||
|
(start 'play (the continue-point cont))
|
||||||
|
)
|
||||||
|
|
||||||
|
(vis-load lev)
|
||||||
|
(set! (-> lev all-visible?) #f)
|
||||||
|
(set! (-> lev force-all-visible?) #t)
|
||||||
|
|
||||||
|
;; reset things
|
||||||
|
;(initialize! *game-info* 'game (the-as game-save #f) (the-as string #f))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
|
@ -1500,61 +1500,6 @@
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
|
|
||||||
(defun bg-custom ((level-name symbol))
|
|
||||||
"Modified version of bg for the PC Port custom levels."
|
|
||||||
|
|
||||||
;; lookup info
|
|
||||||
(format 0 "(bg-custom ~A)%" level-name)
|
|
||||||
(let ((lev-info (lookup-level-info level-name)))
|
|
||||||
(when (= lev-info default-level)
|
|
||||||
(format 0 "Unable to (bg-custom ~A), the level was not found in *level-load-list*~%" level-name)
|
|
||||||
(return #f)
|
|
||||||
)
|
|
||||||
|
|
||||||
;; kill jak (rip)
|
|
||||||
(format 0 "doing stop~%")
|
|
||||||
(stop 'play)
|
|
||||||
|
|
||||||
;; enable visiblity. the custom level won't use it, but we want it on so other levels can be loaded.
|
|
||||||
(set! (-> *level* vis?) #t)
|
|
||||||
|
|
||||||
;; disable border and play mode to prevent loading levels
|
|
||||||
(set! (-> *level* border?) #f)
|
|
||||||
(set! (-> *setting-control* default border-mode) #f)
|
|
||||||
(set! (-> *level* play?) #f)
|
|
||||||
|
|
||||||
(format 0 "doing level load~%")
|
|
||||||
;; allocate level. This may start the loading process, but won't finish it.
|
|
||||||
(let ((lev (level-get-for-use *level* level-name 'active)))
|
|
||||||
(when (not lev)
|
|
||||||
(format 0 "Unable to load level, could not level-get-for-use~%")
|
|
||||||
(return #f)
|
|
||||||
)
|
|
||||||
(format 0 "about to start load loop, game will freeze and hopefully come back soon~%")
|
|
||||||
|
|
||||||
;; spin in a loop and load it. This will cause the game to freeze during the load,
|
|
||||||
;; but this is good enough for now.
|
|
||||||
(while (or (= (-> lev status) 'loading)
|
|
||||||
(= (-> lev status) 'loading-bt)
|
|
||||||
(= (-> lev status) 'login)
|
|
||||||
)
|
|
||||||
(load-continue lev)
|
|
||||||
)
|
|
||||||
|
|
||||||
(when (not (-> lev info continues))
|
|
||||||
(format 0 "level info has no continues, can't load it.~%")
|
|
||||||
)
|
|
||||||
|
|
||||||
(let ((cont (car (-> lev info continues))))
|
|
||||||
(start 'play (the continue-point cont))
|
|
||||||
)
|
|
||||||
|
|
||||||
(vis-load lev)
|
|
||||||
(set! (-> lev all-visible?) #f)
|
|
||||||
(set! (-> lev force-all-visible?) #t)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
(defun play ((use-vis symbol) (init-game symbol))
|
(defun play ((use-vis symbol) (init-game symbol))
|
||||||
"The entry point to the actual game! This allocates the level heaps, loads some data, sets some default parameters and sets the startup level."
|
"The entry point to the actual game! This allocates the level heaps, loads some data, sets some default parameters and sets the startup level."
|
||||||
|
|
|
@ -736,6 +736,10 @@
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
(defmacro print-vector4m (vec &key (dst #t))
|
||||||
|
`(format ,dst "~m ~m ~m ~m~%" (-> ,vec x) (-> ,vec y) (-> ,vec z) (-> ,vec w))
|
||||||
|
)
|
||||||
|
|
||||||
(define *zero-vector* (new 'static 'vector :x 0. :y 0. :z 0. :w 0.))
|
(define *zero-vector* (new 'static 'vector :x 0. :y 0. :z 0. :w 0.))
|
||||||
(define-extern vector-identity! (function vector vector))
|
(define-extern vector-identity! (function vector vector))
|
||||||
(define-extern vector-length (function vector float))
|
(define-extern vector-length (function vector float))
|
||||||
|
|
|
@ -10,6 +10,7 @@ add_library(compiler
|
||||||
build_level/collide_drawable.cpp
|
build_level/collide_drawable.cpp
|
||||||
build_level/collide_pack.cpp
|
build_level/collide_pack.cpp
|
||||||
build_level/color_quantization.cpp
|
build_level/color_quantization.cpp
|
||||||
|
build_level/Entity.cpp
|
||||||
build_level/FileInfo.cpp
|
build_level/FileInfo.cpp
|
||||||
build_level/gltf_mesh_extract.cpp
|
build_level/gltf_mesh_extract.cpp
|
||||||
build_level/LevelFile.cpp
|
build_level/LevelFile.cpp
|
||||||
|
|
159
goalc/build_level/Entity.cpp
Normal file
159
goalc/build_level/Entity.cpp
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
#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"
|
||||||
|
|
||||||
|
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(0); // nav mesh
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
ASSERT_MSG(false, fmt::format("unsupported array type: {}\n", array_type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
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.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();
|
||||||
|
}
|
||||||
|
}
|
36
goalc/build_level/Entity.h
Normal file
36
goalc/build_level/Entity.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "goalc/build_level/ResLump.h"
|
||||||
|
|
||||||
|
#include "third-party/json.hpp"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (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;
|
||||||
|
u32 aid = 0;
|
||||||
|
math::Vector4f trans; // w = 1 here
|
||||||
|
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);
|
|
@ -56,6 +56,15 @@ size_t DrawableTreeArray::add_to_object_file(DataObjectGenerator& gen) const {
|
||||||
return result;
|
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 {
|
std::vector<u8> LevelFile::save_object_file() const {
|
||||||
DataObjectGenerator gen;
|
DataObjectGenerator gen;
|
||||||
gen.add_type_tag("bsp-header");
|
gen.add_type_tag("bsp-header");
|
||||||
|
@ -87,6 +96,7 @@ std::vector<u8> LevelFile::save_object_file() const {
|
||||||
gen.link_word_to_symbol(nickname, 76 / 4);
|
gen.link_word_to_symbol(nickname, 76 / 4);
|
||||||
//(vis-info level-vis-info 8 :offset-assert 80)
|
//(vis-info level-vis-info 8 :offset-assert 80)
|
||||||
//(actors drawable-inline-array-actor :offset-assert 112)
|
//(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)
|
//(cameras (array entity-camera) :offset-assert 116)
|
||||||
//(nodes (inline-array bsp-node) :offset-assert 120)
|
//(nodes (inline-array bsp-node) :offset-assert 120)
|
||||||
//(level level :offset-assert 124)
|
//(level level :offset-assert 124)
|
||||||
|
@ -99,6 +109,7 @@ std::vector<u8> LevelFile::save_object_file() const {
|
||||||
//(unk-data-5 float :offset-assert 164)
|
//(unk-data-5 float :offset-assert 164)
|
||||||
//(adgifs adgif-shader-array :offset-assert 168)
|
//(adgifs adgif-shader-array :offset-assert 168)
|
||||||
//(actor-birth-order (pointer uint32) :offset-assert 172)
|
//(actor-birth-order (pointer uint32) :offset-assert 172)
|
||||||
|
gen.link_word_to_byte(172 / 4, generate_u32_array(actor_birth_order, gen));
|
||||||
//(split-box-indices (pointer uint16) :offset-assert 176)
|
//(split-box-indices (pointer uint16) :offset-assert 176)
|
||||||
//(unk-data-8 uint32 55 :offset-assert 180)
|
//(unk-data-8 uint32 55 :offset-assert 180)
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
#include "goalc/build_level/Entity.h"
|
||||||
#include "goalc/build_level/FileInfo.h"
|
#include "goalc/build_level/FileInfo.h"
|
||||||
#include "goalc/build_level/Tfrag.h"
|
#include "goalc/build_level/Tfrag.h"
|
||||||
#include "goalc/build_level/collide_bvh.h"
|
#include "goalc/build_level/collide_bvh.h"
|
||||||
|
@ -41,10 +42,6 @@ struct TextureId {};
|
||||||
|
|
||||||
struct VisInfo {};
|
struct VisInfo {};
|
||||||
|
|
||||||
struct DrawableActor {};
|
|
||||||
|
|
||||||
struct DrawableInlineArrayActor {};
|
|
||||||
|
|
||||||
struct EntityCamera {};
|
struct EntityCamera {};
|
||||||
|
|
||||||
struct BspNode {};
|
struct BspNode {};
|
||||||
|
@ -94,7 +91,7 @@ struct LevelFile {
|
||||||
std::array<VisInfo, 8> vis_infos;
|
std::array<VisInfo, 8> vis_infos;
|
||||||
|
|
||||||
// (actors drawable-inline-array-actor :offset-assert 112)
|
// (actors drawable-inline-array-actor :offset-assert 112)
|
||||||
DrawableInlineArrayActor actors;
|
std::vector<EntityActor> actors;
|
||||||
|
|
||||||
// (cameras (array entity-camera) :offset-assert 116)
|
// (cameras (array entity-camera) :offset-assert 116)
|
||||||
std::vector<EntityCamera> cameras;
|
std::vector<EntityCamera> cameras;
|
||||||
|
|
|
@ -1,5 +1,314 @@
|
||||||
#include "ResLump.h"
|
#include "ResLump.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "common/util/BitUtils.h"
|
||||||
|
|
||||||
#include "goalc/data_compiler/DataObjectGenerator.h"
|
#include "goalc/data_compiler/DataObjectGenerator.h"
|
||||||
|
|
||||||
#include "third-party/fmt/core.h"
|
#include "third-party/fmt/core.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* name: crate-3141
|
||||||
|
* .symbol name
|
||||||
|
.word 0xce6e6b28
|
||||||
|
.type string
|
||||||
|
.word 0x10000
|
||||||
|
|
||||||
|
scale = 1., 1., 1., 1.,
|
||||||
|
.symbol scale
|
||||||
|
.word 0xce6e6b28
|
||||||
|
.type vector
|
||||||
|
.word 0x80010010
|
||||||
|
|
||||||
|
.symbol visvol
|
||||||
|
.word 0xce6e6b28
|
||||||
|
.type vector
|
||||||
|
.word 0x80020020
|
||||||
|
|
||||||
|
.symbol shadow-mask
|
||||||
|
.word 0xce6e6b28
|
||||||
|
.type uint8
|
||||||
|
.word 0x80010040
|
||||||
|
|
||||||
|
.symbol eco-info
|
||||||
|
.word 0xce6e6b28
|
||||||
|
.type int32
|
||||||
|
.word 0x80020044
|
||||||
|
|
||||||
|
.symbol movie-pos
|
||||||
|
.word 0xce6e6b28
|
||||||
|
.type vector
|
||||||
|
.word 0x80010050
|
||||||
|
.symbol vis-dist
|
||||||
|
.word 0xce6e6b28
|
||||||
|
.type float
|
||||||
|
.word 0x80010060
|
||||||
|
*/
|
||||||
|
|
||||||
|
Res::Res(const std::string& name, float key_frame) : m_name(name), m_key_frame(key_frame) {}
|
||||||
|
|
||||||
|
ResFloat::ResFloat(const std::string& name, const std::vector<float>& values, float key_frame)
|
||||||
|
: Res(name, key_frame), m_values(values) {}
|
||||||
|
|
||||||
|
TagInfo ResFloat::get_tag_info() const {
|
||||||
|
TagInfo result;
|
||||||
|
result.elt_type = "float";
|
||||||
|
result.elt_count = m_values.size();
|
||||||
|
result.inlined = true;
|
||||||
|
result.data_size = m_values.size() * sizeof(float);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResFloat::write_data(DataObjectGenerator& gen) const {
|
||||||
|
for (auto& val : m_values) {
|
||||||
|
gen.add_word_float(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ResFloat::get_alignment() const {
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResInt32::ResInt32(const std::string& name, const std::vector<s32>& values, float key_frame)
|
||||||
|
: Res(name, key_frame), m_values(values) {}
|
||||||
|
|
||||||
|
TagInfo ResInt32::get_tag_info() const {
|
||||||
|
TagInfo result;
|
||||||
|
result.elt_type = "int32";
|
||||||
|
result.elt_count = m_values.size();
|
||||||
|
result.inlined = true;
|
||||||
|
result.data_size = m_values.size() * sizeof(s32);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResInt32::write_data(DataObjectGenerator& gen) const {
|
||||||
|
for (auto& val : m_values) {
|
||||||
|
gen.add_word(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ResInt32::get_alignment() const {
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResUint8::ResUint8(const std::string& name, const std::vector<u8>& values, float key_frame)
|
||||||
|
: Res(name, key_frame), m_values(values) {}
|
||||||
|
|
||||||
|
TagInfo ResUint8::get_tag_info() const {
|
||||||
|
TagInfo result;
|
||||||
|
result.elt_type = "uint8";
|
||||||
|
result.elt_count = m_values.size();
|
||||||
|
result.inlined = true;
|
||||||
|
result.data_size = align4(m_values.size()) * sizeof(u8);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResUint8::write_data(DataObjectGenerator& gen) const {
|
||||||
|
u32 size_words = align4(m_values.size()) / 4;
|
||||||
|
auto offset = gen.current_offset_bytes();
|
||||||
|
for (u32 i = 0; i < size_words; i++) {
|
||||||
|
gen.add_word(0);
|
||||||
|
}
|
||||||
|
memcpy(gen.data() + offset, m_values.data(), m_values.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
int ResUint8::get_alignment() const {
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResVector::ResVector(const std::string& name,
|
||||||
|
const std::vector<math::Vector4f>& values,
|
||||||
|
float key_frame)
|
||||||
|
: Res(name, key_frame), m_values(values) {}
|
||||||
|
|
||||||
|
TagInfo ResVector::get_tag_info() const {
|
||||||
|
TagInfo result;
|
||||||
|
result.elt_type = "vector";
|
||||||
|
result.elt_count = m_values.size();
|
||||||
|
result.inlined = true;
|
||||||
|
result.data_size = m_values.size() * sizeof(math::Vector4f);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResVector::write_data(DataObjectGenerator& gen) const {
|
||||||
|
for (auto& val : m_values) {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
gen.add_word_float(val[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ResVector::get_alignment() const {
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResString::ResString(const std::string& name, const std::vector<std::string>& str, float key_frame)
|
||||||
|
: Res(name, key_frame), m_str(str) {}
|
||||||
|
|
||||||
|
ResString::ResString(const std::string& name, const std::string& str, float key_frame)
|
||||||
|
: Res(name, key_frame), m_str({str}) {}
|
||||||
|
|
||||||
|
TagInfo ResString::get_tag_info() const {
|
||||||
|
TagInfo result;
|
||||||
|
result.elt_type = "string";
|
||||||
|
result.elt_count = m_str.size();
|
||||||
|
result.inlined = false;
|
||||||
|
result.data_size = 4 * m_str.size();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResString::write_data(DataObjectGenerator& gen) const {
|
||||||
|
for (auto& str : m_str) {
|
||||||
|
gen.add_ref_to_string_in_pool(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ResString::get_alignment() const {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResSymbol::ResSymbol(const std::string& name, const std::vector<std::string>& str, float key_frame)
|
||||||
|
: Res(name, key_frame), m_str(str) {}
|
||||||
|
|
||||||
|
ResSymbol::ResSymbol(const std::string& name, const std::string& str, float key_frame)
|
||||||
|
: Res(name, key_frame), m_str({str}) {}
|
||||||
|
|
||||||
|
TagInfo ResSymbol::get_tag_info() const {
|
||||||
|
TagInfo result;
|
||||||
|
result.elt_type = "symbol";
|
||||||
|
result.elt_count = m_str.size();
|
||||||
|
result.inlined = false;
|
||||||
|
result.data_size = 4 * m_str.size();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResSymbol::write_data(DataObjectGenerator& gen) const {
|
||||||
|
for (auto& str : m_str) {
|
||||||
|
gen.add_symbol_link(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ResSymbol::get_alignment() const {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResLump::add_res(std::unique_ptr<Res> res) {
|
||||||
|
m_sorted = false;
|
||||||
|
m_res.emplace_back(std::move(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr int kExtraTagSlots = 10;
|
||||||
|
|
||||||
|
void ResLump::sort_res() {
|
||||||
|
std::stable_sort(m_res.begin(), m_res.end(),
|
||||||
|
[](const std::unique_ptr<Res>& a, const std::unique_ptr<Res>& b) {
|
||||||
|
u64 a_chars = 0;
|
||||||
|
u64 b_chars = 0;
|
||||||
|
auto& a_name = a->name();
|
||||||
|
auto& b_name = b->name();
|
||||||
|
memcpy(&a_chars, a_name.data(), std::min(sizeof(u64), a_name.size()));
|
||||||
|
memcpy(&b_chars, b_name.data(), std::min(sizeof(u64), b_name.size()));
|
||||||
|
return a_chars < b_chars;
|
||||||
|
});
|
||||||
|
m_sorted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ResLump::generate_header(DataObjectGenerator& gen,
|
||||||
|
const std::string& most_specific_type) const {
|
||||||
|
gen.align_to_basic();
|
||||||
|
gen.add_type_tag(most_specific_type);
|
||||||
|
auto result = gen.current_offset_bytes();
|
||||||
|
gen.add_word(m_res.size()); // length;
|
||||||
|
gen.add_word(m_res.size() + kExtraTagSlots); // allocated-length
|
||||||
|
gen.add_word(0); // data base
|
||||||
|
gen.add_word(0); // data top
|
||||||
|
gen.add_word(0); // data size;
|
||||||
|
gen.add_word(0); // extra
|
||||||
|
gen.add_word(0); // tag.
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResLump::generate_tag_list_and_data(DataObjectGenerator& gen, size_t header_to_update) const {
|
||||||
|
ASSERT(m_sorted);
|
||||||
|
gen.align_to_basic();
|
||||||
|
// first is the tag array.
|
||||||
|
const size_t tag_array_start = gen.current_offset_bytes();
|
||||||
|
const size_t tag_array_size = 16 * (m_res.size() + kExtraTagSlots);
|
||||||
|
const size_t tag_array_end = tag_array_start + tag_array_size;
|
||||||
|
|
||||||
|
// next is data
|
||||||
|
const size_t data_start = align16(tag_array_end);
|
||||||
|
size_t current_data_ptr = data_start;
|
||||||
|
|
||||||
|
// on the first pass through, we'll also build these:
|
||||||
|
struct ResRec {
|
||||||
|
size_t align;
|
||||||
|
size_t data;
|
||||||
|
size_t reported_size;
|
||||||
|
};
|
||||||
|
std::vector<ResRec> recs;
|
||||||
|
|
||||||
|
// first pass to write tags and figure out data layout
|
||||||
|
for (auto& res : m_res) {
|
||||||
|
auto& rec = recs.emplace_back();
|
||||||
|
auto alignment = res->get_alignment();
|
||||||
|
while (current_data_ptr % alignment) {
|
||||||
|
current_data_ptr++;
|
||||||
|
}
|
||||||
|
auto tag = res->get_tag_info();
|
||||||
|
|
||||||
|
// name:
|
||||||
|
gen.add_symbol_link(res->name());
|
||||||
|
// key frame
|
||||||
|
gen.add_word_float(res->key_frame());
|
||||||
|
// elt type
|
||||||
|
gen.add_type_tag(tag.elt_type);
|
||||||
|
|
||||||
|
// packed
|
||||||
|
u32 packed = 0;
|
||||||
|
ASSERT(current_data_ptr - data_start < UINT16_MAX);
|
||||||
|
packed |= (u16)(current_data_ptr - data_start);
|
||||||
|
ASSERT(tag.elt_count < (UINT16_MAX >> 1));
|
||||||
|
packed |= (((u16)tag.elt_count) << 16);
|
||||||
|
if (tag.inlined) {
|
||||||
|
packed |= (1 << 31);
|
||||||
|
}
|
||||||
|
gen.add_word(packed);
|
||||||
|
rec.data = current_data_ptr;
|
||||||
|
rec.reported_size = tag.data_size;
|
||||||
|
rec.align = alignment;
|
||||||
|
current_data_ptr += tag.data_size;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < kExtraTagSlots * 4; i++) {
|
||||||
|
gen.add_word(0);
|
||||||
|
}
|
||||||
|
const size_t data_end = current_data_ptr; // todo, does this get rounded up at all?
|
||||||
|
|
||||||
|
gen.align_to_basic();
|
||||||
|
ASSERT(gen.current_offset_bytes() == data_start);
|
||||||
|
current_data_ptr = data_start;
|
||||||
|
|
||||||
|
// second pass to write data
|
||||||
|
for (size_t res_idx = 0; res_idx < m_res.size(); res_idx++) {
|
||||||
|
const auto& res = m_res[res_idx];
|
||||||
|
const auto& rec = recs[res_idx];
|
||||||
|
// pad!
|
||||||
|
while (current_data_ptr % rec.align) {
|
||||||
|
current_data_ptr += 4;
|
||||||
|
gen.add_word(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
res->write_data(gen);
|
||||||
|
ASSERT(gen.current_offset_bytes() - current_data_ptr == rec.reported_size);
|
||||||
|
current_data_ptr = gen.current_offset_bytes();
|
||||||
|
}
|
||||||
|
ASSERT(gen.current_offset_bytes() == data_end);
|
||||||
|
ASSERT(data_end == current_data_ptr);
|
||||||
|
|
||||||
|
// update header
|
||||||
|
gen.link_word_to_byte((header_to_update + 2 * 4) / 4, data_start);
|
||||||
|
gen.link_word_to_byte((header_to_update + 3 * 4) / 4, data_end);
|
||||||
|
gen.set_word((header_to_update + 4 * 4) / 4, data_end - data_start);
|
||||||
|
gen.link_word_to_byte((header_to_update + 6 * 4) / 4, tag_array_start);
|
||||||
|
}
|
|
@ -5,3 +5,134 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "common/math/Vector.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
(deftype res-tag (uint128)
|
||||||
|
((name symbol :offset 0)
|
||||||
|
(key-frame float :offset 32)
|
||||||
|
(elt-type type :offset 64)
|
||||||
|
(data-offset uint16 :offset 96)
|
||||||
|
(elt-count uint32 :offset 112 :size 15)
|
||||||
|
(inlined? uint8 :offset 127 :size 1) ;; guess.
|
||||||
|
)
|
||||||
|
:flag-assert #x900000010
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
|
||||||
|
class DataObjectGenerator;
|
||||||
|
|
||||||
|
struct TagInfo {
|
||||||
|
std::string elt_type;
|
||||||
|
u32 elt_count = 0;
|
||||||
|
bool inlined = false;
|
||||||
|
u32 data_size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Res {
|
||||||
|
public:
|
||||||
|
Res(const std::string& name, float key_frame);
|
||||||
|
const std::string& name() const { return m_name; }
|
||||||
|
float key_frame() const { return m_key_frame; }
|
||||||
|
virtual TagInfo get_tag_info() const = 0;
|
||||||
|
virtual void write_data(DataObjectGenerator& gen) const = 0;
|
||||||
|
virtual int get_alignment() const = 0;
|
||||||
|
virtual ~Res() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_name;
|
||||||
|
float m_key_frame = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ResFloat : public Res {
|
||||||
|
public:
|
||||||
|
ResFloat(const std::string& name, const std::vector<float>& 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<float> m_values;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ResInt32 : public Res {
|
||||||
|
public:
|
||||||
|
ResInt32(const std::string& name, const std::vector<s32>& 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<s32> m_values;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ResUint8 : public Res {
|
||||||
|
public:
|
||||||
|
ResUint8(const std::string& name, const std::vector<u8>& 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<u8> m_values;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ResVector : public Res {
|
||||||
|
public:
|
||||||
|
ResVector(const std::string& name, const std::vector<math::Vector4f>& 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<math::Vector4f> m_values;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ResString : public Res {
|
||||||
|
public:
|
||||||
|
ResString(const std::string& name, const std::vector<std::string>& str, float key_frame);
|
||||||
|
ResString(const std::string& name, const std::string& str, float key_frame);
|
||||||
|
|
||||||
|
TagInfo get_tag_info() const override;
|
||||||
|
void write_data(DataObjectGenerator& gen) const override;
|
||||||
|
int get_alignment() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::string> m_str;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ResSymbol : public Res {
|
||||||
|
public:
|
||||||
|
ResSymbol(const std::string& name, const std::vector<std::string>& str, float key_frame);
|
||||||
|
ResSymbol(const std::string& name, const std::string& str, float key_frame);
|
||||||
|
|
||||||
|
TagInfo get_tag_info() const override;
|
||||||
|
void write_data(DataObjectGenerator& gen) const override;
|
||||||
|
int get_alignment() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::string> m_str;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
(deftype res-lump (basic)
|
||||||
|
((length int32 :offset-assert 4)
|
||||||
|
(allocated-length int32 :offset-assert 8)
|
||||||
|
(data-base pointer :offset-assert 12)
|
||||||
|
(data-top pointer :offset-assert 16)
|
||||||
|
(data-size int32 :offset-assert 20)
|
||||||
|
(extra entity-links :offset-assert 24) ; looks like 0 here
|
||||||
|
(tag (pointer res-tag) :offset-assert 28)
|
||||||
|
*/
|
||||||
|
class ResLump {
|
||||||
|
public:
|
||||||
|
void add_res(std::unique_ptr<Res> res);
|
||||||
|
void sort_res();
|
||||||
|
// extra tag slots seems to be 10, in all cases?
|
||||||
|
size_t generate_header(DataObjectGenerator& gen, const std::string& most_specific_type) const;
|
||||||
|
void generate_tag_list_and_data(DataObjectGenerator& gen, size_t header_to_update) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::unique_ptr<Res>> m_res;
|
||||||
|
bool m_sorted = false;
|
||||||
|
};
|
|
@ -4,6 +4,7 @@
|
||||||
#include "common/util/compress.h"
|
#include "common/util/compress.h"
|
||||||
#include "common/util/json_util.h"
|
#include "common/util/json_util.h"
|
||||||
|
|
||||||
|
#include "goalc/build_level/Entity.h"
|
||||||
#include "goalc/build_level/FileInfo.h"
|
#include "goalc/build_level/FileInfo.h"
|
||||||
#include "goalc/build_level/LevelFile.h"
|
#include "goalc/build_level/LevelFile.h"
|
||||||
#include "goalc/build_level/Tfrag.h"
|
#include "goalc/build_level/Tfrag.h"
|
||||||
|
@ -43,6 +44,8 @@ bool run_build_level(const std::string& input_file, const std::string& output_fi
|
||||||
gltf_mesh_extract::Input mesh_extract_in;
|
gltf_mesh_extract::Input mesh_extract_in;
|
||||||
mesh_extract_in.filename =
|
mesh_extract_in.filename =
|
||||||
file_util::get_file_path({level_json.at("gltf_file").get<std::string>()});
|
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.auto_wall_angle = level_json.value("automatic_wall_angle", 30.0);
|
||||||
mesh_extract_in.tex_pool = &tex_pool;
|
mesh_extract_in.tex_pool = &tex_pool;
|
||||||
gltf_mesh_extract::Output mesh_extract_out;
|
gltf_mesh_extract::Output mesh_extract_out;
|
||||||
gltf_mesh_extract::extract(mesh_extract_in, mesh_extract_out);
|
gltf_mesh_extract::extract(mesh_extract_in, mesh_extract_out);
|
||||||
|
@ -61,13 +64,19 @@ bool run_build_level(const std::string& input_file, const std::string& output_fi
|
||||||
file.nickname = level_json.at("nickname").get<std::string>();
|
file.nickname = level_json.at("nickname").get<std::string>();
|
||||||
// vis infos
|
// vis infos
|
||||||
// actors
|
// actors
|
||||||
|
std::vector<EntityActor> actors;
|
||||||
|
add_actors_from_json(level_json.at("actors"), actors, 1234);
|
||||||
|
file.actors = std::move(actors);
|
||||||
// cameras
|
// cameras
|
||||||
// nodes
|
// nodes
|
||||||
// boxes
|
// boxes
|
||||||
// ambients
|
// ambients
|
||||||
// subdivs
|
// subdivs
|
||||||
// adgifs
|
// adgifs
|
||||||
// actor birht
|
// actor birth
|
||||||
|
for (size_t i = 0; i < file.actors.size(); i++) {
|
||||||
|
file.actor_birth_order.push_back(i);
|
||||||
|
}
|
||||||
// split box
|
// split box
|
||||||
|
|
||||||
// add stuff to the PC level structure
|
// add stuff to the PC level structure
|
||||||
|
|
|
@ -32,7 +32,9 @@ struct CNode {
|
||||||
struct BBox {
|
struct BBox {
|
||||||
math::Vector3f mins, maxs;
|
math::Vector3f mins, maxs;
|
||||||
std::string sz_to_string() const {
|
std::string sz_to_string() const {
|
||||||
return fmt::format("({})", ((maxs - mins) / 4096.f).to_string_aligned());
|
return fmt::format("{} {} ({})", (mins / 4096.f).to_string_aligned(),
|
||||||
|
(maxs / 4096.f).to_string_aligned(),
|
||||||
|
((maxs - mins) / 4096.f).to_string_aligned());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -79,10 +81,54 @@ void update_bsphere_recursive(const CNode& node, const math::Vector3f& origin, f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void collect_vertices(const CNode& node, std::vector<math::Vector3f>& verts) {
|
||||||
|
for (auto& child : node.child_nodes) {
|
||||||
|
collect_vertices(child, verts);
|
||||||
|
}
|
||||||
|
for (auto& face : node.faces) {
|
||||||
|
verts.push_back(face.v[0]);
|
||||||
|
verts.push_back(face.v[1]);
|
||||||
|
verts.push_back(face.v[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t find_most_distant(math::Vector3f pt, const std::vector<math::Vector3f>& verts) {
|
||||||
|
float max_dist_squared = 0;
|
||||||
|
size_t idx_of_best = 0;
|
||||||
|
for (size_t i = 0; i < verts.size(); i++) {
|
||||||
|
float dist = (pt - verts[i]).squared_length();
|
||||||
|
if (dist > max_dist_squared) {
|
||||||
|
max_dist_squared = dist;
|
||||||
|
idx_of_best = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return idx_of_best;
|
||||||
|
}
|
||||||
|
|
||||||
|
void compute_my_bsphere_ritters(CNode& node) {
|
||||||
|
std::vector<math::Vector3f> verts;
|
||||||
|
collect_vertices(node, verts);
|
||||||
|
ASSERT(verts.size() > 0);
|
||||||
|
auto px = verts[0];
|
||||||
|
auto py = verts[find_most_distant(px, verts)];
|
||||||
|
auto pz = verts[find_most_distant(py, verts)];
|
||||||
|
|
||||||
|
auto origin = (pz + py) / 2.f;
|
||||||
|
node.bsphere.x() = origin.x();
|
||||||
|
node.bsphere.y() = origin.y();
|
||||||
|
node.bsphere.z() = origin.z();
|
||||||
|
|
||||||
|
float max_squared = 0;
|
||||||
|
for (auto& pt : verts) {
|
||||||
|
max_squared = std::max(max_squared, (pt - origin).squared_length());
|
||||||
|
}
|
||||||
|
node.bsphere.w() = std::sqrt(max_squared);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Compute the bsphere of a single node.
|
* Compute the bsphere of a single node.
|
||||||
*/
|
*/
|
||||||
void compute_my_bsphere(CNode& node) {
|
BBox compute_my_bsphere_bad(CNode& node) {
|
||||||
// first compute bbox.
|
// first compute bbox.
|
||||||
BBox bbox = bbox_of_node(node);
|
BBox bbox = bbox_of_node(node);
|
||||||
float r = 0;
|
float r = 0;
|
||||||
|
@ -92,6 +138,7 @@ void compute_my_bsphere(CNode& node) {
|
||||||
node.bsphere.y() = origin.y();
|
node.bsphere.y() = origin.y();
|
||||||
node.bsphere.z() = origin.z();
|
node.bsphere.z() = origin.z();
|
||||||
node.bsphere.w() = std::sqrt(r);
|
node.bsphere.w() = std::sqrt(r);
|
||||||
|
return bbox;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -114,6 +161,7 @@ void split_along_dim(std::vector<CollideFace>& faces,
|
||||||
* Split a node into two nodes. The outputs should be uninitialized nodes
|
* Split a node into two nodes. The outputs should be uninitialized nodes
|
||||||
*/
|
*/
|
||||||
void split_node_once(CNode& node, CNode* out0, CNode* out1) {
|
void split_node_once(CNode& node, CNode* out0, CNode* out1) {
|
||||||
|
compute_my_bsphere_ritters(node);
|
||||||
CNode temps[6];
|
CNode temps[6];
|
||||||
// split_along_dim(node.faces, pick_dim_for_split(node.faces), &out0->faces, &out1->faces);
|
// split_along_dim(node.faces, pick_dim_for_split(node.faces), &out0->faces, &out1->faces);
|
||||||
split_along_dim(node.faces, 0, &temps[0].faces, &temps[1].faces);
|
split_along_dim(node.faces, 0, &temps[0].faces, &temps[1].faces);
|
||||||
|
@ -121,10 +169,11 @@ void split_node_once(CNode& node, CNode* out0, CNode* out1) {
|
||||||
split_along_dim(node.faces, 2, &temps[4].faces, &temps[5].faces);
|
split_along_dim(node.faces, 2, &temps[4].faces, &temps[5].faces);
|
||||||
node.faces.clear();
|
node.faces.clear();
|
||||||
for (auto& t : temps) {
|
for (auto& t : temps) {
|
||||||
compute_my_bsphere(t);
|
compute_my_bsphere_ritters(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
float max_bspheres[3] = {0, 0, 0};
|
float max_bspheres[3] = {0, 0, 0};
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
max_bspheres[i] = std::max(temps[i * 2].bsphere.w(), temps[i * 2 + 1].bsphere.w());
|
max_bspheres[i] = std::max(temps[i * 2].bsphere.w(), temps[i * 2 + 1].bsphere.w());
|
||||||
}
|
}
|
||||||
|
@ -206,7 +255,7 @@ void split_as_needed(CNode& root) {
|
||||||
num_leaves *= 8;
|
num_leaves *= 8;
|
||||||
lg::info("after splitting, the worst leaf has {} tris, {} radius", worst.max_leaf_count,
|
lg::info("after splitting, the worst leaf has {} tris, {} radius", worst.max_leaf_count,
|
||||||
worst.max_bsphere_w / 4096.f);
|
worst.max_bsphere_w / 4096.f);
|
||||||
if (worst.max_leaf_count < MAX_FACES_IN_FRAG && worst.max_bsphere_w < (100.f * 4096.f)) {
|
if (worst.max_leaf_count < MAX_FACES_IN_FRAG && worst.max_bsphere_w < (125.f * 4096.f)) {
|
||||||
need_to_split = false;
|
need_to_split = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,7 +268,7 @@ void split_as_needed(CNode& root) {
|
||||||
* (note that we don't do bspheres of bspheres... I think this is better?)
|
* (note that we don't do bspheres of bspheres... I think this is better?)
|
||||||
*/
|
*/
|
||||||
void bsphere_recursive(CNode& node) {
|
void bsphere_recursive(CNode& node) {
|
||||||
compute_my_bsphere(node);
|
compute_my_bsphere_ritters(node);
|
||||||
for (auto& child : node.child_nodes) {
|
for (auto& child : node.child_nodes) {
|
||||||
bsphere_recursive(child);
|
bsphere_recursive(child);
|
||||||
}
|
}
|
||||||
|
|
|
@ -665,7 +665,7 @@ std::optional<std::vector<CollideFace>> subdivide_face_if_needed(CollideFace fac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void extract(const Input& /*in*/,
|
void extract(const Input& in,
|
||||||
CollideOutput& out,
|
CollideOutput& out,
|
||||||
const tinygltf::Model& model,
|
const tinygltf::Model& model,
|
||||||
const std::vector<NodeWithTransform>& all_nodes) {
|
const std::vector<NodeWithTransform>& all_nodes) {
|
||||||
|
@ -718,7 +718,7 @@ void extract(const Input& /*in*/,
|
||||||
}
|
}
|
||||||
|
|
||||||
face.bsphere = math::bsphere_of_triangle(face.v);
|
face.bsphere = math::bsphere_of_triangle(face.v);
|
||||||
face.bsphere.w() += 1e-1;
|
face.bsphere.w() += 1e-1 * 5;
|
||||||
for (int j = 0; j < 3; j++) {
|
for (int j = 0; j < 3; j++) {
|
||||||
float output_dist = face.bsphere.w() - (face.bsphere.xyz() - face.v[j]).length();
|
float output_dist = face.bsphere.w() - (face.bsphere.xyz() - face.v[j]).length();
|
||||||
if (output_dist < 0) {
|
if (output_dist < 0) {
|
||||||
|
@ -748,6 +748,21 @@ void extract(const Input& /*in*/,
|
||||||
}
|
}
|
||||||
out.faces = std::move(fixed_faces);
|
out.faces = std::move(fixed_faces);
|
||||||
|
|
||||||
|
if (in.auto_wall_enable) {
|
||||||
|
lg::info("automatically detecting walls with angle {}", in.auto_wall_angle);
|
||||||
|
int wall_count = 0;
|
||||||
|
float wall_cos = std::cos(in.auto_wall_angle * 2.f * 3.14159 / 360.f);
|
||||||
|
for (auto& face : out.faces) {
|
||||||
|
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);
|
||||||
|
wall_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lg::info("automatic wall: {}/{} converted to walls", wall_count, out.faces.size());
|
||||||
|
}
|
||||||
|
|
||||||
lg::info("{} out of {} faces appeared to have wrong orientation and were flipped",
|
lg::info("{} out of {} faces appeared to have wrong orientation and were flipped",
|
||||||
suspicious_faces, out.faces.size());
|
suspicious_faces, out.faces.size());
|
||||||
lg::info("{} faces were too big and were subdivided", fix_count);
|
lg::info("{} faces were too big and were subdivided", fix_count);
|
||||||
|
|
|
@ -13,6 +13,8 @@ struct Input {
|
||||||
std::string filename;
|
std::string filename;
|
||||||
TexturePool* tex_pool = nullptr;
|
TexturePool* tex_pool = nullptr;
|
||||||
bool get_colors = true;
|
bool get_colors = true;
|
||||||
|
bool auto_wall_enable = true;
|
||||||
|
float auto_wall_angle = 30.f;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TfragOutput {
|
struct TfragOutput {
|
||||||
|
|
Loading…
Reference in a new issue