[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:
water111 2022-06-27 20:37:51 -04:00 committed by GitHub
parent fa3f4b0a88
commit 54937a95b1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 841 additions and 69 deletions

View file

@ -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"
}
}
]
} }

View file

@ -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))
)
)
)

View file

@ -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."

View file

@ -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))

View file

@ -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

View 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();
}
}

View 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);

View file

@ -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)

View file

@ -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;

View file

@ -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);
}

View file

@ -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;
};

View file

@ -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

View file

@ -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);
} }

View file

@ -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);

View file

@ -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 {