mirror of
https://github.com/open-goal/jak-project
synced 2026-08-03 17:02:47 -04:00
custom levels: support camera entities and custom keyframe values (#4310)
This commit is contained in:
@@ -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:
|
||||
//
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -26176,7 +26176,7 @@
|
||||
)
|
||||
|
||||
(deftype entity-camera (entity)
|
||||
((connect connectable :inline :offset-assert 64)
|
||||
((quat quaternion :inline :offset-assert 64)
|
||||
)
|
||||
:flag-assert #x1b00000050
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
)
|
||||
|
||||
@@ -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))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -135,7 +135,7 @@ that gets accessed by the accompanying process."
|
||||
)
|
||||
|
||||
(deftype entity-camera (entity)
|
||||
((connect connectable :inline)
|
||||
((quat quaternion :inline)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -45,8 +45,6 @@ struct TextureId {};
|
||||
|
||||
struct VisInfo {};
|
||||
|
||||
struct EntityCamera {};
|
||||
|
||||
struct BspNode {};
|
||||
|
||||
struct Box8s {};
|
||||
|
||||
@@ -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
@@ -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
@@ -142,7 +142,7 @@
|
||||
|
||||
;; definition of type entity-camera
|
||||
(deftype entity-camera (entity)
|
||||
((connect connectable :inline)
|
||||
((quat quaternion :inline)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
+1
-1
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user