custom levels: support camera entities and custom keyframe values (#4310)

This commit is contained in:
Hat Kid
2026-06-13 00:29:24 +02:00
committed by GitHub
parent feaa7fb873
commit 41fd0b6cec
26 changed files with 256 additions and 52 deletions
@@ -26,6 +26,9 @@
// float types: float, meters (1 meter = 4096.0 units), degrees (65536.0 = 360°)
// vector types: vector (normal floats), vector4m (meters), vector3m (meters with w set to 1.0), vector-vol (normal floats with w in meters), movie-pos (meters with w in degrees)
// special types: symbol, type, string, eco-info, cell-info, buzzer-info, water-height
// by default, lumps will always use the default keyframe value of -1000000000.0 (DEFAULT_RES_TIME).
// most of the actor code will be using that value, but there are some exceptions (camera entities, water volumes, etc.)
// you can specify a different keyframe by suffixing the lump type with e.g. "@0.5", so for a float at keyframe 0.5, the type would be "float@0.5".
//
// examples:
//
@@ -137,6 +140,50 @@
}
],
// Camera entities you want to use in your level. These are used to dynamically adjust camera settings/modes when Jak enters a volume.
// There are a couple of different camera modes, depending on what lumps are present:
// - cam-circular: orbits a specified point. requires "pivot" lump.
// - optional lumps: "maxAngle", "focalPull"
// - cam-standoff: requires "align" lump.
// - cam-string: the default camera mode. used when any of these lumps are present to override the default cam mode behavior:
// - "stringMaxLength", "stringMinLength", "stringMaxHeight", "stringMinHeight"
//
// Some generic lumps that can be used for all camera modes:
// - "fov": set the camera fov (default is 64).
// - "interpTime": set the cam blend time when entering the camera entity volume.
// - "tiltAdjust": set the camera roll value.
//
// There's a "flags" lump that takes a cam-slave-options enum value:
// "flags": ["enum-uint32", "(cam-slave-options SAME_SIDE COLLIDE)"]
//
// There's three different volume types (these all have to be defined in keyframe 0, e.g. ["vector-vol@0.0": ...]):
// vol: sets camera entity when Jak is inside.
// pvol: preferred volume when camera entities overlap.
// cutoutvol: disables camera entity when Jak is inside.
//
// "interesting" is an optional vector lump that can be set to be used as a "point of interest" for the camera to focus on.
"cameras": [
{
"trans": [17.26, 9.0, 13.2],
"quat": [0, 1, 0, 0],
"lump": {
"name": "test-cam",
"flags": ["enum-uint32", "(cam-slave-options SAME_SIDE)"],
"pivot": ["vector3m", [15.0761, 2.6482, 25.548]],
"interpTime": ["float", 1.0],
"vol": [
"vector-vol@0",
[-0.09046, 0.025624, 0.99557, 32.528575],
[-0.995854, 0.007292, -0.090673, -8.671137],
[0.09046, -0.025624, -0.99557, -16.528584],
[0.995854, -0.007292, 0.090673, 24.671144],
[-0.009584, -0.999645, 0.024858, 0.695884],
[0.009584, 0.999645, -0.024858, 15.304117]
]
}
}
],
"actors": [
{
"trans": [-21.6238, 20.0496, 17.1191], // translation
@@ -26,6 +26,9 @@
// float types: float, meters (1 meter = 4096.0 units), degrees (65536.0 = 360°)
// vector types: vector (normal floats), vector4m (meters), vector3m (meters with w set to 1.0), vector-vol (normal floats with w in meters), movie-pos (meters with w in degrees)
// special types: symbol, type, string, eco-info, cell-info, buzzer-info, water-height
// by default, lumps will always use the default keyframe value of -1000000000.0 (DEFAULT_RES_TIME).
// most of the actor code will be using that value, but there are some exceptions (camera entities, water volumes, etc.)
// you can specify a different keyframe by suffixing the lump type with e.g. "@0.5", so for a float at keyframe 0.5, the type would be "float@0.5".
//
// examples:
//
@@ -26,6 +26,9 @@
// float types: float, meters (1 meter = 4096.0 units), degrees (65536.0 = 360°)
// vector types: vector (normal floats), vector4m (meters), vector3m (meters with w set to 1.0), vector-vol (normal floats with w in meters), movie-pos (meters with w in degrees)
// special types: symbol, type, string, eco-info, cell-info, buzzer-info, water-height
// by default, lumps will always use the default keyframe value of -1000000000.0 (DEFAULT_RES_TIME).
// most of the actor code will be using that value, but there are some exceptions (camera entities, water volumes, etc.)
// you can specify a different keyframe by suffixing the lump type with e.g. "@0.5", so for a float at keyframe 0.5, the type would be "float@0.5".
//
// examples:
//
+1 -1
View File
@@ -14549,7 +14549,7 @@
(deftype entity-camera (entity)
(
(connect connectable :inline :offset-assert 64)
(quat quaternion :inline :offset-assert 64)
)
:method-count-assert 27
:size-assert #x50
+1 -1
View File
@@ -22506,7 +22506,7 @@ Levels store a reference to this which holds all the entities in the level."
)
(deftype entity-camera (entity)
((connect connectable :inline :offset-assert 64)) ;; guess from jak 1
((quat quaternion :inline :offset-assert 64))
:flag-assert #x1b00000050
)
+1 -1
View File
@@ -26176,7 +26176,7 @@
)
(deftype entity-camera (entity)
((connect connectable :inline :offset-assert 64)
((quat quaternion :inline :offset-assert 64)
)
:flag-assert #x1b00000050
)
+1 -1
View File
@@ -641,7 +641,7 @@
(matrix-rotate-z! s1-1 (- (-> s5-0 z)))
(matrix*! sv-192 s1-1 sv-192)
(matrix->quaternion (the-as quaternion s0-1) sv-192)
(quaternion-inverse! (the-as quaternion s2-0) (the-as quaternion (-> self cam-entity connect)))
(quaternion-inverse! (the-as quaternion s2-0) (-> self cam-entity quat))
(quaternion*! (the-as quaternion s2-0) (the-as quaternion s0-1) (the-as quaternion s2-0))
(quaternion-normalize! (the-as quaternion s2-0))))))
(set! *camera-read-analog* #f)
+7 -1
View File
@@ -48,6 +48,7 @@
(:methods
(update-perm! (_type_ symbol entity-perm-status) _type_)))
(deftype entity-links (structure)
((prev-link entity-links)
(next-link entity-links)
@@ -63,14 +64,17 @@
(:methods
(birth? (_type_ vector) symbol)))
(deftype entity-perm-array (inline-array-class)
((data entity-perm :inline :dynamic)))
(set! (-> entity-perm-array heap-base) (the-as uint 16))
(deftype entity-links-array (inline-array-class)
((data entity-links :inline :dynamic)))
(set! (-> entity-links-array heap-base) (the-as uint 64))
(deftype entity (res-lump)
@@ -84,7 +88,7 @@
(get-level (_type_) level)))
(deftype entity-camera (entity)
((connect connectable :inline)))
((quat quaternion :inline)))
(deftype entity-ambient-data (structure)
((user-object object 3)
@@ -99,9 +103,11 @@
(user-int8 int8 12 :overlay-at (-> user-object 0))
(user-uint8 uint8 12 :overlay-at (-> user-object 0))))
(deftype entity-ambient-data-array (inline-array-class)
((data entity-ambient-data :inline :dynamic)))
(set! (-> entity-ambient-data-array heap-base) (the-as uint 16))
(deftype entity-ambient (entity)
+1 -1
View File
@@ -889,7 +889,7 @@
(matrix-rotate-z! s1-1 (- (-> s5-0 z)))
(matrix*! sv-192 s1-1 sv-192)
(matrix->quaternion (the-as quaternion s0-1) sv-192)
(quaternion-inverse! (the-as quaternion s2-0) (the-as quaternion (-> self cam-entity connect)))
(quaternion-inverse! (the-as quaternion s2-0) (-> self cam-entity quat))
(quaternion*! (the-as quaternion s2-0) (the-as quaternion s0-1) (the-as quaternion s2-0))
(quaternion-normalize! (the-as quaternion s2-0))
)
+11 -1
View File
@@ -69,6 +69,7 @@
(define *generate-actor-vis-output* #f)
(deftype entity-perm (structure)
"Entity information that gets persisted in the save file."
((user-object object 2)
(user-uint64 uint64 :overlay-at (-> user-object 0))
(user-float float 2 :overlay-at (-> user-object 0))
@@ -91,6 +92,7 @@
(deftype entity-links (structure)
"A linked list of entities with some extra data about their current status."
((prev-link entity-links)
(next-link entity-links)
(entity entity)
@@ -120,6 +122,8 @@
(set! (-> entity-perm-array heap-base) (the-as uint 16))
(deftype entity-links-array (inline-array-class)
"An array of [[entity-links]].
Levels store a reference to this which holds all the entities in the level."
((data entity-links :inline :dynamic)
)
)
@@ -128,6 +132,11 @@
(set! (-> entity-links-array heap-base) (the-as uint 64))
(deftype entity (res-lump)
"Entities are mainly used to place objects in a level.
As a child of [[res-lump]], they store various types of metadata
in [[res-tag]]s (such as name, curve data, volume data, etc.)
that gets accessed by the accompanying process."
((trans vector :inline)
(aid uint32)
)
@@ -141,7 +150,7 @@
)
(deftype entity-camera (entity)
((connect connectable :inline)
((quat quaternion :inline)
)
)
@@ -164,6 +173,7 @@
)
(deftype entity-actor (entity)
"Child class of [[entity]] used to spawn [[process-drawable]] actors."
((etype type :offset 56)
(task game-task)
(kill-mask task-mask :offset 52)
+1 -1
View File
@@ -910,7 +910,7 @@
(matrix-rotate-z! s1-1 (- (-> s5-0 z)))
(matrix*! sv-192 s1-1 sv-192)
(matrix->quaternion s0-1 sv-192)
(quaternion-inverse! (the-as quaternion s2-0) (the-as quaternion (-> self cam-entity connect)))
(quaternion-inverse! (the-as quaternion s2-0) (-> self cam-entity quat))
(quaternion*! (the-as quaternion s2-0) s0-1 (the-as quaternion s2-0))
(quaternion-normalize! (the-as quaternion s2-0))
)
+1 -1
View File
@@ -3046,7 +3046,7 @@
(let ((gp-0 (the-as entity-camera (entity-by-name (-> *camera* settings cam-slope)))))
(when gp-0
(set! (-> self slope-adjust target) (res-lump-float gp-0 'scale-factor :default 0.6))
(quaternion-copy! (-> self slope-quat) (the-as quaternion (-> gp-0 connect)))
(quaternion-copy! (-> self slope-quat) (-> gp-0 quat))
)
)
)
+1 -1
View File
@@ -135,7 +135,7 @@ that gets accessed by the accompanying process."
)
(deftype entity-camera (entity)
((connect connectable :inline)
((quat quaternion :inline)
)
)
+51 -24
View File
@@ -101,79 +101,86 @@ std::vector<T> enum_from_json(const nlohmann::json& json, decompiler::Decompiler
return result;
}
static std::unordered_map<std::string,
std::function<std::unique_ptr<Res>(const std::string&,
const nlohmann::json&,
decompiler::DecompilerTypeSystem&)>>
static std::unordered_map<
std::string,
std::function<std::unique_ptr<
Res>(const std::string&, const nlohmann::json&, float, decompiler::DecompilerTypeSystem&)>>
lump_map = {
// integers
{"int32",
[](const std::string& name,
const nlohmann::json& json,
float time,
decompiler::DecompilerTypeSystem& dts) {
(void)dts;
std::vector<s32> data;
for (size_t i = 1; i < json.size(); i++) {
data.push_back(json[i].get<int>());
}
return std::make_unique<ResInt32>(name, data, DEFAULT_RES_TIME);
return std::make_unique<ResInt32>(name, data, time);
}},
{"uint32",
[](const std::string& name,
const nlohmann::json& json,
float time,
decompiler::DecompilerTypeSystem& dts) {
(void)dts;
std::vector<u32> data;
for (size_t i = 1; i < json.size(); i++) {
data.push_back(json[i].get<u32>());
}
return std::make_unique<ResUint32>(name, data, DEFAULT_RES_TIME);
return std::make_unique<ResUint32>(name, data, time);
}},
{"enum-int32",
[](const std::string& name,
const nlohmann::json& json,
float time,
decompiler::DecompilerTypeSystem& dts) {
std::vector<s32> data;
for (size_t i = 1; i < json.size(); i++) {
data = enum_from_json<s32>(json[i], dts);
}
return std::make_unique<ResInt32>(name, data, DEFAULT_RES_TIME);
return std::make_unique<ResInt32>(name, data, time);
}},
{"enum-uint32",
[](const std::string& name,
const nlohmann::json& json,
float time,
decompiler::DecompilerTypeSystem& dts) {
std::vector<u32> data;
for (size_t i = 1; i < json.size(); i++) {
data = enum_from_json<u32>(json[i], dts);
}
return std::make_unique<ResUint32>(name, data, DEFAULT_RES_TIME);
return std::make_unique<ResUint32>(name, data, time);
}},
// special lumps
{"eco-info",
[](const std::string& name,
const nlohmann::json& json,
float time,
decompiler::DecompilerTypeSystem& dts) {
std::vector<s32> data;
// pickup-type
data.push_back(static_cast<s32>(get_enum_val(json[1].get<std::string>(), dts)));
// amount
data.push_back(static_cast<s32>(get_enum_or_int(json[2], dts)));
return std::make_unique<ResInt32>(name, data, DEFAULT_RES_TIME);
return std::make_unique<ResInt32>(name, data, time);
}},
{"cell-info",
[](const std::string& name,
const nlohmann::json& json,
float time,
decompiler::DecompilerTypeSystem& dts) {
std::vector<s32> data;
// (pickup-type fuel-cell)
data.push_back(6);
data.push_back(static_cast<s32>(get_enum_or_int(json[1], dts)));
return std::make_unique<ResInt32>(name, data, DEFAULT_RES_TIME);
return std::make_unique<ResInt32>(name, data, time);
}},
{"buzzer-info",
[](const std::string& name,
const nlohmann::json& json,
float time,
decompiler::DecompilerTypeSystem& dts) {
std::vector<s32> data;
// (pickup-type buzzer)
@@ -181,11 +188,12 @@ static std::unordered_map<std::string,
auto task = static_cast<s32>(get_enum_val(json[1].get<std::string>(), dts));
auto buzzer = json[2].get<int>();
data.push_back(task + (buzzer * (1 << 16)));
return std::make_unique<ResInt32>(name, data, DEFAULT_RES_TIME);
return std::make_unique<ResInt32>(name, data, time);
}},
{"water-height",
[](const std::string& name,
const nlohmann::json& json,
float time,
decompiler::DecompilerTypeSystem& dts) {
std::vector<float> data;
// water-height
@@ -200,129 +208,140 @@ static std::unordered_map<std::string,
if (json.size() >= 6) {
data.push_back(json[5].get<float>() * METER_LENGTH);
}
return std::make_unique<ResFloat>(name, data, DEFAULT_RES_TIME);
return std::make_unique<ResFloat>(name, data, time);
}},
{"symbol",
[](const std::string& name,
const nlohmann::json& json,
float time,
decompiler::DecompilerTypeSystem& dts) {
(void)dts;
std::vector<std::string> data;
for (size_t i = 1; i < json.size(); i++) {
data.push_back(json[i].get<std::string>());
}
return std::make_unique<ResSymbol>(name, data, DEFAULT_RES_TIME);
return std::make_unique<ResSymbol>(name, data, time);
}},
{"type",
[](const std::string& name,
const nlohmann::json& json,
float time,
decompiler::DecompilerTypeSystem& dts) {
(void)dts;
std::vector<std::string> data;
for (size_t i = 1; i < json.size(); i++) {
data.push_back(json[i].get<std::string>());
}
return std::make_unique<ResType>(name, data, DEFAULT_RES_TIME);
return std::make_unique<ResType>(name, data, time);
}},
{"string",
[](const std::string& name,
const nlohmann::json& json,
float time,
decompiler::DecompilerTypeSystem& dts) {
(void)dts;
std::vector<std::string> data;
for (size_t i = 1; i < json.size(); i++) {
data.push_back(json[i].get<std::string>());
}
return std::make_unique<ResString>(name, data, DEFAULT_RES_TIME);
return std::make_unique<ResString>(name, data, time);
}},
// vectors
{"vector",
[](const std::string& name,
const nlohmann::json& json,
float time,
decompiler::DecompilerTypeSystem& dts) {
(void)dts;
std::vector<math::Vector4f> data;
for (size_t i = 1; i < json.size(); i++) {
data.push_back(vector_from_json(json[i]));
}
return std::make_unique<ResVector>(name, data, DEFAULT_RES_TIME);
return std::make_unique<ResVector>(name, data, time);
}},
{"vector4m",
[](const std::string& name,
const nlohmann::json& json,
float time,
decompiler::DecompilerTypeSystem& dts) {
(void)dts;
std::vector<math::Vector4f> data;
for (size_t i = 1; i < json.size(); i++) {
data.push_back(vectorm4_from_json(json[i]));
}
return std::make_unique<ResVector>(name, data, DEFAULT_RES_TIME);
return std::make_unique<ResVector>(name, data, time);
}},
{"vector3m",
[](const std::string& name,
const nlohmann::json& json,
float time,
decompiler::DecompilerTypeSystem& dts) {
(void)dts;
std::vector<math::Vector4f> data;
for (size_t i = 1; i < json.size(); i++) {
data.push_back(vectorm3_from_json(json[i]));
}
return std::make_unique<ResVector>(name, data, DEFAULT_RES_TIME);
return std::make_unique<ResVector>(name, data, time);
}},
{"movie-pos",
[](const std::string& name,
const nlohmann::json& json,
float time,
decompiler::DecompilerTypeSystem& dts) {
(void)dts;
std::vector<math::Vector4f> data;
for (size_t i = 1; i < json.size(); i++) {
data.push_back(movie_pos_from_json(json[i]));
}
return std::make_unique<ResVector>(name, data, DEFAULT_RES_TIME);
return std::make_unique<ResVector>(name, data, time);
}},
{"vector-vol",
[](const std::string& name,
const nlohmann::json& json,
float time,
decompiler::DecompilerTypeSystem& dts) {
(void)dts;
std::vector<math::Vector4f> data;
for (size_t i = 1; i < json.size(); i++) {
data.push_back(vector_vol_from_json(json[i]));
}
return std::make_unique<ResVector>(name, data, DEFAULT_RES_TIME);
return std::make_unique<ResVector>(name, data, time);
}},
// floats
{"float",
[](const std::string& name,
const nlohmann::json& json,
float time,
decompiler::DecompilerTypeSystem& dts) {
(void)dts;
std::vector<float> data;
for (size_t i = 1; i < json.size(); i++) {
data.push_back(json[i].get<float>());
}
return std::make_unique<ResFloat>(name, data, DEFAULT_RES_TIME);
return std::make_unique<ResFloat>(name, data, time);
}},
{"meters",
[](const std::string& name,
const nlohmann::json& json,
float time,
decompiler::DecompilerTypeSystem& dts) {
(void)dts;
std::vector<float> data;
for (size_t i = 1; i < json.size(); i++) {
data.push_back(json[i].get<float>() * METER_LENGTH);
}
return std::make_unique<ResFloat>(name, data, DEFAULT_RES_TIME);
return std::make_unique<ResFloat>(name, data, time);
}},
{"degrees", [](const std::string& name,
const nlohmann::json& json,
float time,
decompiler::DecompilerTypeSystem& dts) {
(void)dts;
std::vector<float> data;
for (size_t i = 1; i < json.size(); i++) {
data.push_back(json[i].get<float>() * DEGREES_LENGTH);
}
return std::make_unique<ResFloat>(name, data, DEFAULT_RES_TIME);
return std::make_unique<ResFloat>(name, data, time);
}}};
std::unique_ptr<Res> res_from_json_array(const std::string& name,
@@ -338,8 +357,16 @@ std::unique_ptr<Res> res_from_json_array(const std::string& name,
lump.type_name()));
}
auto array_type = lump.get<std::string>();
float keyframe = DEFAULT_RES_TIME;
auto at_pos = array_type.find('@');
if (at_pos != std::string::npos) {
keyframe = std::stof(array_type.substr(at_pos + 1));
array_type = array_type.substr(0, at_pos);
}
if (lump_map.find(array_type) != lump_map.end()) {
return lump_map[array_type](name, json_array, dts);
return lump_map[array_type](name, json_array, keyframe, dts);
} else {
throw std::runtime_error(
fmt::format("unsupported array type for lump {}: {}\n", name, array_type));
+67
View File
@@ -193,4 +193,71 @@ void add_ambients_from_json(const nlohmann::json& json,
ambient.res_lump.sort_res();
}
}
size_t EntityCamera::generate(DataObjectGenerator& gen) const {
size_t result = res_lump.generate_header(gen, "entity-camera");
for (int i = 0; i < 4; i++) {
gen.add_word_float(trans[i]);
}
gen.add_word(aid);
gen.add_word(0);
gen.add_word(0);
gen.add_word(0);
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_cameras_array(DataObjectGenerator& gen, const std::vector<EntityCamera>& cameras) {
std::vector<size_t> camera_locs;
for (auto& cam : cameras) {
camera_locs.push_back(cam.generate(gen));
}
gen.align_to_basic();
gen.add_type_tag("array");
size_t result = gen.current_offset_bytes();
gen.add_word(cameras.size());
gen.add_word(cameras.size());
gen.add_type_tag("entity-camera");
for (size_t i = 0; i < cameras.size(); i++) {
gen.link_word_to_byte(gen.add_word(0), camera_locs[i]);
}
gen.align(4);
return result;
}
void add_cameras_from_json(const nlohmann::json& json,
std::vector<EntityCamera>& camera_list,
u32 base_aid,
decompiler::DecompilerTypeSystem& dts) {
for (const auto& cam_json : json) {
auto& cam = camera_list.emplace_back();
cam.aid = cam_json.value("aid", base_aid + (u32)camera_list.size());
cam.trans = vectorm3_from_json(cam_json.at("trans"));
cam.quat = math::Vector4f(0, 1, 0, 0);
if (cam_json.find("quat") != cam_json.end()) {
cam.quat = vector_from_json(cam_json.at("quat"));
}
if (cam_json.find("lump") != cam_json.end()) {
for (const auto& [key, value] : cam_json.at("lump").items()) {
if (value.is_string()) {
std::string value_string = value.get<std::string>();
if (value_string.size() > 0 && value_string[0] == '\'') {
cam.res_lump.add_res(
std::make_unique<ResSymbol>(key, value_string.substr(1), DEFAULT_RES_TIME));
} else {
cam.res_lump.add_res(std::make_unique<ResString>(key, value_string, DEFAULT_RES_TIME));
}
continue;
}
if (value.is_array()) {
cam.res_lump.add_res(res_from_json_array(key, value, dts));
}
}
}
cam.res_lump.sort_res();
}
}
} // namespace jak1
+15
View File
@@ -50,4 +50,19 @@ void add_ambients_from_json(const nlohmann::json& json,
std::vector<EntityAmbient>& ambient_list,
u32 base_aid,
decompiler::DecompilerTypeSystem& dts);
struct EntityCamera {
ResLump res_lump;
u32 aid = 0;
math::Vector4f trans;
math::Vector4f quat;
size_t generate(DataObjectGenerator& gen) const;
};
size_t generate_cameras_array(DataObjectGenerator& gen, const std::vector<EntityCamera>& cameras);
void add_cameras_from_json(const nlohmann::json& json,
std::vector<EntityCamera>& camera_list,
u32 base_aid,
decompiler::DecompilerTypeSystem& dts);
} // namespace jak1
+3 -1
View File
@@ -125,7 +125,7 @@ std::vector<u8> LevelFile::save_object_file() const {
auto file_info_slot = info.add_to_object_file(gen);
gen.link_word_to_byte(1, file_info_slot);
ambient_arr_slot = jak1::generate_inline_array_ambients(gen, ambients);
ambient_arr_slot = generate_inline_array_ambients(gen, ambients);
//(bsphere vector :inline :offset-assert 16)
//(all-visible-list (pointer uint16) :offset-assert 32)
@@ -153,6 +153,8 @@ std::vector<u8> LevelFile::save_object_file() const {
//(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)
if (!cameras.empty())
gen.link_word_to_byte(116 / 4, generate_cameras_array(gen, cameras));
//(nodes (inline-array bsp-node) :offset-assert 120)
//(level level :offset-assert 124)
//(current-leaf-idx uint16 :offset-assert 128)
-2
View File
@@ -45,8 +45,6 @@ struct TextureId {};
struct VisInfo {};
struct EntityCamera {};
struct BspNode {};
struct Box8s {};
+34 -8
View File
@@ -55,28 +55,54 @@ bool run_build_level(const std::string& input_file,
file.nickname = level_json.at("nickname").get<std::string>();
// vis infos
// actors
std::vector<u32> aid_list;
std::vector<EntityActor> actors;
auto dts = decompiler::DecompilerTypeSystem(GameVersion::Jak1);
dts.parse_type_defs({"decompiler", "config", "jak1", "all-types.gc"});
add_actors_from_json(level_json.at("actors"), actors, level_json.value("base_id", 1234), dts);
std::sort(actors.begin(), actors.end(), [](auto& a, auto& b) { return a.aid < b.aid; });
auto duplicates = std::adjacent_find(actors.begin(), actors.end(),
[](auto& a, auto& b) { return a.aid == b.aid; });
ASSERT_MSG(duplicates == actors.end(),
std::ranges::sort(actors, [](auto& a, auto& b) { return a.aid < b.aid; });
for (auto& actor : actors) {
aid_list.push_back(actor.aid);
}
auto duplicate_actors =
std::ranges::adjacent_find(actors, [](auto& a, auto& b) { return a.aid == b.aid; });
ASSERT_MSG(duplicate_actors == actors.end(),
fmt::format("Actor IDs must be unique. Found at least two actors with ID {}",
duplicates->aid));
duplicate_actors->aid));
file.actors = std::move(actors);
// ambients
std::vector<EntityAmbient> ambients;
jak1::add_ambients_from_json(level_json.at("ambients"), ambients,
level_json.value("base_id", 12345), dts);
add_ambients_from_json(level_json.at("ambients"), ambients, level_json.value("base_id", 12345),
dts);
file.ambients = std::move(ambients);
auto& ambient_drawable_tree = file.drawable_trees.ambients.emplace_back();
(void)ambient_drawable_tree;
// cameras
if (level_json.contains("cameras")) {
std::vector<EntityCamera> cameras;
add_cameras_from_json(
level_json.at("cameras"), cameras,
std::max(level_json.value("base_id", 2345), (int)*std::ranges::max_element(aid_list)), dts);
std::ranges::sort(cameras, [](auto& a, auto& b) { return a.aid < b.aid; });
for (auto& cam : cameras) {
aid_list.push_back(cam.aid);
}
std::ranges::sort(aid_list, [](auto& a, auto& b) { return a < b; });
auto duplicate_cams =
std::ranges::adjacent_find(cameras, [](auto& a, auto& b) { return a.aid == b.aid; });
ASSERT_MSG(
duplicate_cams == cameras.end(),
fmt::format("Actor IDs must be unique. Found at least two camera entities with ID {}",
duplicate_cams->aid));
auto duplicate_aids =
std::ranges::adjacent_find(aid_list, [](auto& a, auto& b) { return a == b; });
ASSERT_MSG(duplicate_aids == aid_list.end(),
fmt::format("Actor IDs must be unique. Found one actor and camera entity with ID {}",
*duplicate_aids));
file.cameras = std::move(cameras);
}
// nodes
// boxes
// ambients
// subdivs
// adgifs
// actor birth
+1 -1
View File
@@ -1060,7 +1060,7 @@
(matrix-rotate-z! s1-1 (- (-> s5-0 z)))
(matrix*! sv-192 s1-1 sv-192)
(matrix->quaternion (the-as quaternion s0-1) sv-192)
(quaternion-inverse! (the-as quaternion s2-0) (the-as quaternion (-> self cam-entity connect)))
(quaternion-inverse! (the-as quaternion s2-0) (-> self cam-entity quat))
(quaternion*! (the-as quaternion s2-0) (the-as quaternion s0-1) (the-as quaternion s2-0))
(quaternion-normalize! (the-as quaternion s2-0))
)
+1 -1
View File
@@ -142,7 +142,7 @@
;; definition of type entity-camera
(deftype entity-camera (entity)
((connect connectable :inline)
((quat quaternion :inline)
)
)
+1 -1
View File
@@ -1093,7 +1093,7 @@
(matrix-rotate-z! s1-1 (- (-> s5-0 z)))
(matrix*! sv-192 s1-1 sv-192)
(matrix->quaternion (the-as quaternion s0-1) sv-192)
(quaternion-inverse! (the-as quaternion s2-0) (the-as quaternion (-> self cam-entity connect)))
(quaternion-inverse! (the-as quaternion s2-0) (-> self cam-entity quat))
(quaternion*! (the-as quaternion s2-0) (the-as quaternion s0-1) (the-as quaternion s2-0))
(quaternion-normalize! (the-as quaternion s2-0))
)
+1 -1
View File
@@ -234,7 +234,7 @@ that gets accessed by the accompanying process."
;; definition of type entity-camera
(deftype entity-camera (entity)
((connect connectable :inline)
((quat quaternion :inline)
)
)
+1 -1
View File
@@ -1054,7 +1054,7 @@
(matrix-rotate-z! s1-1 (- (-> s5-0 z)))
(matrix*! sv-192 s1-1 sv-192)
(matrix->quaternion s0-1 sv-192)
(quaternion-inverse! (the-as quaternion s2-0) (the-as quaternion (-> self cam-entity connect)))
(quaternion-inverse! (the-as quaternion s2-0) (-> self cam-entity quat))
(quaternion*! (the-as quaternion s2-0) s0-1 (the-as quaternion s2-0))
(quaternion-normalize! (the-as quaternion s2-0))
)
+1 -1
View File
@@ -3171,7 +3171,7 @@
(let ((gp-0 (the-as entity-camera (entity-by-name (-> *camera* settings cam-slope)))))
(when gp-0
(set! (-> self slope-adjust target) (res-lump-float gp-0 'scale-factor :default 0.6))
(quaternion-copy! (-> self slope-quat) (the-as quaternion (-> gp-0 connect)))
(quaternion-copy! (-> self slope-quat) (-> gp-0 quat))
)
)
)
+1 -1
View File
@@ -234,7 +234,7 @@ that gets accessed by the accompanying process."
;; definition of type entity-camera
(deftype entity-camera (entity)
((connect connectable :inline)
((quat quaternion :inline)
)
)