[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,
// 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/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
)
(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))
"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-extern vector-identity! (function vector vector))
(define-extern vector-length (function vector float))

View file

@ -10,6 +10,7 @@ add_library(compiler
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

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;
}
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");
@ -87,6 +96,7 @@ std::vector<u8> LevelFile::save_object_file() const {
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)
@ -99,6 +109,7 @@ std::vector<u8> LevelFile::save_object_file() const {
//(unk-data-5 float :offset-assert 164)
//(adgifs adgif-shader-array :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));
//(split-box-indices (pointer uint16) :offset-assert 176)
//(unk-data-8 uint32 55 :offset-assert 180)

View file

@ -6,6 +6,7 @@
#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/collide_bvh.h"
@ -41,10 +42,6 @@ struct TextureId {};
struct VisInfo {};
struct DrawableActor {};
struct DrawableInlineArrayActor {};
struct EntityCamera {};
struct BspNode {};
@ -94,7 +91,7 @@ struct LevelFile {
std::array<VisInfo, 8> vis_infos;
// (actors drawable-inline-array-actor :offset-assert 112)
DrawableInlineArrayActor actors;
std::vector<EntityActor> actors;
// (cameras (array entity-camera) :offset-assert 116)
std::vector<EntityCamera> cameras;

View file

@ -1,5 +1,314 @@
#include "ResLump.h"
#include <algorithm>
#include "common/util/BitUtils.h"
#include "goalc/data_compiler/DataObjectGenerator.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 "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/json_util.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"
@ -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;
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.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);
@ -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>();
// vis infos
// actors
std::vector<EntityActor> actors;
add_actors_from_json(level_json.at("actors"), actors, 1234);
file.actors = std::move(actors);
// cameras
// nodes
// boxes
// ambients
// subdivs
// adgifs
// actor birht
// actor birth
for (size_t i = 0; i < file.actors.size(); i++) {
file.actor_birth_order.push_back(i);
}
// split box
// add stuff to the PC level structure

View file

@ -32,7 +32,9 @@ struct CNode {
struct BBox {
math::Vector3f mins, maxs;
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.
*/
void compute_my_bsphere(CNode& node) {
BBox compute_my_bsphere_bad(CNode& node) {
// first compute bbox.
BBox bbox = bbox_of_node(node);
float r = 0;
@ -92,6 +138,7 @@ void compute_my_bsphere(CNode& node) {
node.bsphere.y() = origin.y();
node.bsphere.z() = origin.z();
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
*/
void split_node_once(CNode& node, CNode* out0, CNode* out1) {
compute_my_bsphere_ritters(node);
CNode temps[6];
// 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);
@ -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);
node.faces.clear();
for (auto& t : temps) {
compute_my_bsphere(t);
compute_my_bsphere_ritters(t);
}
float max_bspheres[3] = {0, 0, 0};
for (int i = 0; i < 3; i++) {
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;
lg::info("after splitting, the worst leaf has {} tris, {} radius", worst.max_leaf_count,
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;
}
}
@ -219,7 +268,7 @@ void split_as_needed(CNode& root) {
* (note that we don't do bspheres of bspheres... I think this is better?)
*/
void bsphere_recursive(CNode& node) {
compute_my_bsphere(node);
compute_my_bsphere_ritters(node);
for (auto& child : node.child_nodes) {
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,
const tinygltf::Model& model,
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.w() += 1e-1;
face.bsphere.w() += 1e-1 * 5;
for (int j = 0; j < 3; j++) {
float output_dist = face.bsphere.w() - (face.bsphere.xyz() - face.v[j]).length();
if (output_dist < 0) {
@ -748,6 +748,21 @@ void extract(const Input& /*in*/,
}
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",
suspicious_faces, out.faces.size());
lg::info("{} faces were too big and were subdivided", fix_count);

View file

@ -13,6 +13,8 @@ struct Input {
std::string filename;
TexturePool* tex_pool = nullptr;
bool get_colors = true;
bool auto_wall_enable = true;
float auto_wall_angle = 30.f;
};
struct TfragOutput {