mirror of
https://github.com/open-goal/jak-project
synced 2026-08-08 10:34:30 -04:00
clean up cam-combiner
This commit is contained in:
@@ -3149,6 +3149,110 @@ bool try_to_rewrite_vector_inline_ctor(const Env& env,
|
||||
return false;
|
||||
}
|
||||
|
||||
struct Jak1MatrixRowDeref {
|
||||
RegisterAccess base;
|
||||
std::vector<DerefToken> matrix_tokens;
|
||||
};
|
||||
|
||||
std::optional<RegisterAccess> identity_var(Form* form) {
|
||||
auto* elt = form ? form->try_as_element<SimpleExpressionElement>() : nullptr;
|
||||
if (!elt || elt->expr().kind() != SimpleExpression::Kind::IDENTITY ||
|
||||
!elt->expr().get_arg(0).is_var()) {
|
||||
return {};
|
||||
}
|
||||
return elt->expr().get_arg(0).var();
|
||||
}
|
||||
|
||||
std::optional<TypeSpec> deref_result_type(RegisterAccess base,
|
||||
const std::vector<DerefToken>& tokens,
|
||||
const Env& env) {
|
||||
TypeSpec current = env.get_variable_type(base, true);
|
||||
for (const auto& token : tokens) {
|
||||
if (token.kind() == DerefToken::Kind::FIELD_NAME) {
|
||||
try {
|
||||
current = env.dts->ts.lookup_field_info(current.base_type(), token.field_name()).type;
|
||||
} catch (const std::exception&) {
|
||||
return {};
|
||||
}
|
||||
} else if (token.kind() == DerefToken::Kind::INTEGER_CONSTANT ||
|
||||
token.kind() == DerefToken::Kind::INTEGER_EXPRESSION) {
|
||||
if (!current.has_single_arg()) {
|
||||
return {};
|
||||
}
|
||||
// get_single_arg() refers into current, so copy it before assignment destroys the argument
|
||||
// storage.
|
||||
const auto element_type = current.get_single_arg();
|
||||
current = element_type;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
std::optional<Jak1MatrixRowDeref> match_jak1_matrix_row(Form* form, int row, const Env& env) {
|
||||
auto* deref = form ? form->try_as_element<DerefElement>() : nullptr;
|
||||
if (!deref || deref->is_addr_of() || deref->tokens().size() < 3) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto suffix = deref->tokens().size() - 3;
|
||||
if (!deref->tokens().at(suffix).is_field_name("vector") ||
|
||||
!deref->tokens().at(suffix + 1).is_int(row) ||
|
||||
!deref->tokens().at(suffix + 2).is_field_name("quad")) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto base = identity_var(deref->base());
|
||||
if (!base) {
|
||||
return {};
|
||||
}
|
||||
std::vector<DerefToken> matrix_tokens(deref->tokens().begin(), deref->tokens().begin() + suffix);
|
||||
auto matrix_type = deref_result_type(*base, matrix_tokens, env);
|
||||
if (!matrix_type || *matrix_type != TypeSpec("matrix")) {
|
||||
return {};
|
||||
}
|
||||
return Jak1MatrixRowDeref{*base, std::move(matrix_tokens)};
|
||||
}
|
||||
|
||||
bool same_deref_tokens(std::vector<DerefToken> lhs, std::vector<DerefToken> rhs, const Env& env) {
|
||||
if (lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < lhs.size(); ++i) {
|
||||
if (lhs.at(i).kind() != rhs.at(i).kind()) {
|
||||
return false;
|
||||
}
|
||||
switch (lhs.at(i).kind()) {
|
||||
case DerefToken::Kind::FIELD_NAME:
|
||||
if (lhs.at(i).field_name() != rhs.at(i).field_name()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case DerefToken::Kind::INTEGER_CONSTANT:
|
||||
if (lhs.at(i).int_constant() != rhs.at(i).int_constant()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case DerefToken::Kind::INTEGER_EXPRESSION:
|
||||
if (lhs.at(i).expr()->to_string(env) != rhs.at(i).expr()->to_string(env)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Form* append_deref_tokens(Form* base, const std::vector<DerefToken>& tokens, FormPool& pool) {
|
||||
if (tokens.empty()) {
|
||||
return base;
|
||||
}
|
||||
return pool.form<DerefElement>(base, false, tokens);
|
||||
}
|
||||
|
||||
bool try_to_rewrite_matrix_inline_copy(const Env& env, FormPool& pool, FormStack& stack) {
|
||||
if (env.func->name() == "matrix-copy!" ||
|
||||
env.func->name() == "(method 63 collide-shape-moving)") {
|
||||
@@ -3160,49 +3264,58 @@ bool try_to_rewrite_matrix_inline_copy(const Env& env, FormPool& pool, FormStack
|
||||
// first, check the loads. they should be something like source = (-> MAT vec quad)
|
||||
// MAT should always be a variable
|
||||
std::vector<RegisterAccess> load_src_ras, store_dest_ras, store_src_ras;
|
||||
std::vector<DerefToken> src_matrix_tokens, dst_matrix_tokens;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Matcher deref_matcher;
|
||||
if (env.version == GameVersion::Jak1) {
|
||||
deref_matcher = Matcher::deref(
|
||||
Matcher::any_reg(0), false,
|
||||
{DerefTokenMatcher::string("vector"), DerefTokenMatcher::integer(i),
|
||||
DerefTokenMatcher::string("quad")});
|
||||
auto row = match_jak1_matrix_row(matrix_entries->at(i).source, i, env);
|
||||
if (!row || (i && !same_deref_tokens(src_matrix_tokens, row->matrix_tokens, env))) {
|
||||
return false;
|
||||
}
|
||||
load_src_ras.push_back(row->base);
|
||||
if (!i) {
|
||||
src_matrix_tokens = std::move(row->matrix_tokens);
|
||||
}
|
||||
} else {
|
||||
const char* names[] = {"rvec", "uvec", "fvec", "trans"};
|
||||
deref_matcher =
|
||||
Matcher::deref(Matcher::any_reg(0), false,
|
||||
{DerefTokenMatcher::string(names[i]),
|
||||
DerefTokenMatcher::string("quad")});
|
||||
auto deref_matcher = Matcher::deref(
|
||||
Matcher::any_reg(0), false,
|
||||
{DerefTokenMatcher::string(names[i]), DerefTokenMatcher::string("quad")});
|
||||
auto mr = match(deref_matcher, matrix_entries->at(i).source, &env);
|
||||
if (!mr.matched) {
|
||||
return false;
|
||||
}
|
||||
load_src_ras.push_back(mr.maps.regs.at(0).value());
|
||||
}
|
||||
auto mr = match(deref_matcher, matrix_entries->at(i).source, &env);
|
||||
if (!mr.matched) {
|
||||
return false;
|
||||
}
|
||||
load_src_ras.push_back(mr.maps.regs.at(0).value());
|
||||
}
|
||||
|
||||
// check the stores
|
||||
for (int i = 4; i < 8; i++) {
|
||||
Matcher dst_matcher;
|
||||
if (env.version == GameVersion::Jak1) {
|
||||
dst_matcher = Matcher::deref(
|
||||
Matcher::any_reg(0), false,
|
||||
{DerefTokenMatcher::string("vector"), DerefTokenMatcher::integer(i - 4),
|
||||
DerefTokenMatcher::string("quad")});
|
||||
auto* set = dynamic_cast<SetFormFormElement*>(matrix_entries->at(i).elt);
|
||||
auto row = set ? match_jak1_matrix_row(set->dst(), i - 4, env) : std::nullopt;
|
||||
auto src = set ? identity_var(set->src()) : std::nullopt;
|
||||
if (!row || !src ||
|
||||
(i != 4 && !same_deref_tokens(dst_matrix_tokens, row->matrix_tokens, env))) {
|
||||
return false;
|
||||
}
|
||||
store_dest_ras.push_back(row->base);
|
||||
store_src_ras.push_back(*src);
|
||||
if (i == 4) {
|
||||
dst_matrix_tokens = std::move(row->matrix_tokens);
|
||||
}
|
||||
} else {
|
||||
const char* names[] = {"rvec", "uvec", "fvec", "trans"};
|
||||
dst_matcher =
|
||||
Matcher::deref(Matcher::any_reg(0), false,
|
||||
{DerefTokenMatcher::string(names[i - 4]),
|
||||
DerefTokenMatcher::string("quad")});
|
||||
auto dst_matcher = Matcher::deref(
|
||||
Matcher::any_reg(0), false,
|
||||
{DerefTokenMatcher::string(names[i - 4]), DerefTokenMatcher::string("quad")});
|
||||
Matcher matcher = Matcher::set(dst_matcher, Matcher::any_reg(1));
|
||||
auto mr = match(matcher, matrix_entries->at(i).elt, &env);
|
||||
if (!mr.matched) {
|
||||
return false;
|
||||
}
|
||||
store_dest_ras.push_back(mr.maps.regs.at(0).value());
|
||||
store_src_ras.push_back(mr.maps.regs.at(1).value());
|
||||
}
|
||||
Matcher matcher = Matcher::set(dst_matcher, Matcher::any_reg(1));
|
||||
auto mr = match(matcher, matrix_entries->at(i).elt, &env);
|
||||
if (!mr.matched) {
|
||||
return false;
|
||||
}
|
||||
store_dest_ras.push_back(mr.maps.regs.at(0).value());
|
||||
store_src_ras.push_back(mr.maps.regs.at(1).value());
|
||||
}
|
||||
|
||||
// check loads are all loading from the same matrix
|
||||
@@ -3226,13 +3339,15 @@ bool try_to_rewrite_matrix_inline_copy(const Env& env, FormPool& pool, FormStack
|
||||
}
|
||||
}
|
||||
|
||||
// check types
|
||||
if (env.get_variable_type(load_src_ras.at(0), true) != TypeSpec("matrix")) {
|
||||
return false;
|
||||
}
|
||||
// For Jak 1, match_jak1_matrix_row checked the types after following the dereference prefix.
|
||||
if (env.version != GameVersion::Jak1) {
|
||||
if (env.get_variable_type(load_src_ras.at(0), true) != TypeSpec("matrix")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (env.get_variable_type(store_dest_ras.at(0), true) != TypeSpec("matrix")) {
|
||||
return false;
|
||||
if (env.get_variable_type(store_dest_ras.at(0), true) != TypeSpec("matrix")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
stack.pop(8);
|
||||
@@ -3248,6 +3363,7 @@ bool try_to_rewrite_matrix_inline_copy(const Env& env, FormPool& pool, FormStack
|
||||
} else {
|
||||
// lg::info(" popped src: {}", src_repopped->to_string(env));
|
||||
}
|
||||
src_repopped = append_deref_tokens(src_repopped, src_matrix_tokens, pool);
|
||||
|
||||
// src_repopped = matrix_entries->at(0).source;
|
||||
bool found = false;
|
||||
@@ -3261,8 +3377,11 @@ bool try_to_rewrite_matrix_inline_copy(const Env& env, FormPool& pool, FormStack
|
||||
// lg::info(" popped dst: {} {} {}", dst_repopped->to_string(env), ra.reg().to_string(),
|
||||
// ra.mode() == AccessMode::WRITE);
|
||||
}
|
||||
dst_repopped = append_deref_tokens(dst_repopped, dst_matrix_tokens, pool);
|
||||
|
||||
if (found) {
|
||||
// If the matrix is a field of the popped value, the copy result is not the value of that
|
||||
// containing object. Emit it as a standalone side-effecting form instead.
|
||||
if (found && dst_matrix_tokens.empty()) {
|
||||
stack.push_value_to_reg(
|
||||
ra,
|
||||
pool.form<GenericElement>(
|
||||
@@ -7094,7 +7213,19 @@ void TypeOfElement::update_from_stack(const Env& env,
|
||||
std::vector<FormElement*>* result,
|
||||
bool allow_side_effects) {
|
||||
mark_popped();
|
||||
value->update_children_from_stack(env, pool, stack, allow_side_effects);
|
||||
// The CFG rewrite replaces several uses in the expanded runtime-type check with this single
|
||||
// expression. The original register-use analysis therefore does not mark the input as consumed,
|
||||
// even though rtype-of is now its last use. Supply the post-rewrite consumption explicitly so a
|
||||
// one-use input can still be propagated into rtype-of.
|
||||
auto value_var = identity_var(value);
|
||||
if (value_var) {
|
||||
value =
|
||||
pop_to_forms({*value_var}, env, pool, stack, allow_side_effects, RegSet{value_var->reg()})
|
||||
.at(0);
|
||||
value->parent_element = this;
|
||||
} else {
|
||||
value->update_children_from_stack(env, pool, stack, allow_side_effects);
|
||||
}
|
||||
result->push_back(this);
|
||||
}
|
||||
////////////////////////
|
||||
|
||||
@@ -17405,6 +17405,30 @@ its matching records and each allocation bitmap begins with every slot free."
|
||||
|
||||
(defenum cam-slave-options
|
||||
:bitfield #t
|
||||
:type uint32
|
||||
(BUTT_CAM)
|
||||
(SAME_SIDE)
|
||||
(MOVE_SPHERICAL)
|
||||
(ALLOW_Z_ROT)
|
||||
(JUMP_PITCHES)
|
||||
(COLLIDE)
|
||||
(FIND_HIDDEN_TARGET)
|
||||
(DRAG)
|
||||
(PLAYER_MOVING_CAMERA)
|
||||
(LINE_OF_SIGHT)
|
||||
(MOVEMENT_BLOCKED)
|
||||
(SHRINK_MAX_ANGLE)
|
||||
(GOTO_GOOD_POINT)
|
||||
(BLOCK_SHIFT_BUTTONS)
|
||||
(BIKE_MODE)
|
||||
(NO_ROTATE)
|
||||
(STICKY_ANGLE)
|
||||
(AIR_EXIT)
|
||||
)
|
||||
|
||||
(defenum cam-slave-options-i
|
||||
:bitfield #t
|
||||
:type int32
|
||||
(BUTT_CAM)
|
||||
(SAME_SIDE)
|
||||
(MOVE_SPHERICAL)
|
||||
@@ -17440,6 +17464,25 @@ its matching records and each allocation bitmap begins with every slot free."
|
||||
(between 3)
|
||||
)
|
||||
|
||||
(defenum cam-track-status
|
||||
:type uint64
|
||||
(use-slave-tracking 0)
|
||||
(track-at-combiner 1)
|
||||
(track-at-dst 2)
|
||||
(track-at-src 3))
|
||||
|
||||
(defenum cam-master-options
|
||||
:type uint32
|
||||
:bitfield #t
|
||||
(ignore-regions 0)
|
||||
(have-target 1)
|
||||
(switch-only-on-ground 2)
|
||||
(set-combiner-axis 3)
|
||||
(flip-combiner 4)
|
||||
(have-ease-to-pos 5)
|
||||
(in-base-region 6)
|
||||
)
|
||||
|
||||
;; - Types
|
||||
|
||||
;; Shared gameplay-camera tuning used for collision movement, input response,
|
||||
@@ -17641,8 +17684,8 @@ its matching records and each allocation bitmap begins with every slot free."
|
||||
(dist-from-dest float :offset-assert 208)
|
||||
(flip-control-axis vector :inline :offset-assert 224)
|
||||
(velocity vector :inline :offset-assert 240)
|
||||
(tracking-status uint64 :offset-assert 256)
|
||||
(tracking-options int32 :offset-assert 264)
|
||||
(tracking-status cam-track-status :offset-assert 256)
|
||||
(tracking-options cam-slave-options-i :offset-assert 264)
|
||||
(tracking cam-rotation-tracker :inline :offset-assert 272)
|
||||
)
|
||||
:heap-base #x170
|
||||
@@ -17675,7 +17718,7 @@ its matching records and each allocation bitmap begins with every slot free."
|
||||
(circular-follow vector :inline :offset-assert 2176)
|
||||
(max-angle-offset float :offset-assert 2192)
|
||||
(max-angle-curr float :offset-assert 2196)
|
||||
(options uint32 :offset-assert 2200)
|
||||
(options cam-slave-options :offset-assert 2200)
|
||||
(cam-entity entity :offset-assert 2204) ; not totally confirmed yet, could be entity-actor
|
||||
(velocity vector :inline :offset-assert 2208)
|
||||
(desired-pos vector :inline :offset-assert 2224)
|
||||
@@ -17745,7 +17788,7 @@ its matching records and each allocation bitmap begins with every slot free."
|
||||
;; active slaves, target transforms and tracking trail, and transition state;
|
||||
;; the combiner produces the final rendered camera.
|
||||
(deftype camera-master (process)
|
||||
((master-options uint32 :offset-assert 112)
|
||||
((master-options cam-master-options :offset-assert 112)
|
||||
(num-slaves int32 :offset-assert 116)
|
||||
(slave (pointer camera-slave) 2 :offset-assert 120)
|
||||
(slave-options uint32 :offset-assert 128)
|
||||
@@ -24540,8 +24583,8 @@ optional bottom depth. Enable ordinary water particles after loading the data."
|
||||
lead with a quartic transition." (function cam-rotation-tracker vector symbol vector))
|
||||
(define-extern slave-set-rotation! "Build tracker's inverse camera rotation from its follow point,
|
||||
optional point of interest and tilt; keep the target in frame, optionally blend toward the new
|
||||
orientation, and remove roll. options-bits is a raw cam-slave-options word carried in a float."
|
||||
(function cam-rotation-tracker vector float float symbol none))
|
||||
orientation, and remove roll."
|
||||
(function cam-rotation-tracker vector cam-slave-options float symbol none))
|
||||
(define-extern camera-slave-debug (function camera-slave none))
|
||||
(define-extern camera-line-rel-len (function vector vector float vector4w none))
|
||||
(define-extern cam-slave-get-flags "Read property's base flag word, set bits from property-on,
|
||||
@@ -30392,14 +30435,6 @@ from the entity name, then load the art group named by the entity type."))
|
||||
(gouraud 2)
|
||||
)
|
||||
|
||||
;; Known camera-master settings exposed on the debug menu.
|
||||
(defenum cam-master-options
|
||||
:type uint64
|
||||
:bitfield #t
|
||||
(ignore-regions 0)
|
||||
(switch-only-on-ground 2)
|
||||
)
|
||||
|
||||
;; - Functions
|
||||
|
||||
(define-extern build-continue-menu
|
||||
|
||||
@@ -309,6 +309,8 @@
|
||||
// that they used the correct number. This will override the decompiler's
|
||||
// automatic detection.
|
||||
"bad_format_strings": {
|
||||
"ERROR <GMJ>: missing camera-slave parameter to *camera-combiner* start-tracking~%": 1,
|
||||
"ERROR <GMJ>: missing camera-slave parameter to *camera-combiner* copy-tracking~%": 1,
|
||||
"ERROR: dma tag has data in reserved bits ~X~%": 0,
|
||||
"#<surface f0:~m f1:~f tf+:~f tf-:~f sf:~f tvv:~m": 5,
|
||||
"ERROR<GMJ>: value of symbol ~A in task-controls is not a task-control~%": 0,
|
||||
|
||||
@@ -1353,7 +1353,7 @@
|
||||
],
|
||||
"(event cam-combiner-active)": [
|
||||
[10, "a0", "vector"],
|
||||
[[99, 127], "gp", "camera-slave"],
|
||||
[[99, 128], "gp", "camera-slave"],
|
||||
[[187, 231], "gp", "camera-slave"]
|
||||
],
|
||||
"(method 15 tracking-spline)": [
|
||||
|
||||
@@ -6826,28 +6826,8 @@
|
||||
},
|
||||
"(event cam-combiner-active)": {
|
||||
"vars": {
|
||||
"t9-2": "print-error-fn",
|
||||
"a0-15": "output-stream",
|
||||
"a1-3": "error-message",
|
||||
"v1-7": "argument",
|
||||
"t9-3": "type-check-fn",
|
||||
"v1-8": "argument",
|
||||
"t9-4": "print-error-fn",
|
||||
"a0-18": "output-stream",
|
||||
"a1-5": "error-message",
|
||||
"v1-10": "argument",
|
||||
"gp-1": ["source-slave", "camera-slave"],
|
||||
"gp-2": ["source-position", "vector"],
|
||||
"t9-10": "print-error-fn",
|
||||
"a0-27": "output-stream",
|
||||
"a1-11": "error-message",
|
||||
"v1-23": "argument",
|
||||
"t9-11": "type-check-fn",
|
||||
"v1-24": "argument",
|
||||
"t9-12": "print-error-fn",
|
||||
"a0-30": "output-stream",
|
||||
"a1-13": "error-message",
|
||||
"v1-25": "argument",
|
||||
"gp-3": "source-slave",
|
||||
"a2-17": "destination-tracker",
|
||||
"a3-3": "source-tracker",
|
||||
|
||||
@@ -29,23 +29,23 @@
|
||||
|
||||
;; DECOMP BEGINS
|
||||
|
||||
;; Animation alignment turns motion authored on the skeleton's alignment joint
|
||||
;; into movement of the owning process. Each update samples the align joint and
|
||||
;; measures translation, scale, and rotation relative to the previous sample.
|
||||
;; The delta is disabled when the animation changes or crosses a
|
||||
;; loop boundary, where subtracting the two samples would create a false jump.
|
||||
;; Align-control moves a process through an animation.
|
||||
;; The "align" joint of the animation defines how the root of the process
|
||||
;; drawable should move.
|
||||
;; Each update samples the align joint and measures translation, scale, and
|
||||
;; rotation relative to the previous sample.
|
||||
;; matrix and transform use index 0 for the current sample and index 1 for the
|
||||
;; previous sample.
|
||||
|
||||
(deftype align-control (basic)
|
||||
((flags align-flags)
|
||||
(process process-drawable)
|
||||
(frame-group art-joint-anim) ;; animation sampled during the previous update
|
||||
(frame-num float) ;; frame sampled during the previous update
|
||||
(matrix matrix 2 :inline) ;; current and previous alignment-joint matrices
|
||||
(frame-group art-joint-anim) ;; animation sampled during the previous update
|
||||
(frame-num float) ;; frame sampled during the previous update
|
||||
(matrix matrix 2 :inline) ;; current and previous alignment-joint matrices
|
||||
(transform transformq 2 :inline) ;; decomposed current and previous samples
|
||||
(delta transformq :inline) ;; current sample relative to the previous sample
|
||||
(last-speed meters) ;; last horizontal alignment speed
|
||||
(delta transformq :inline) ;; current sample relative to the previous sample
|
||||
(last-speed meters) ;; last horizontal alignment speed
|
||||
;; Working transformq view of the current transform.
|
||||
(align transformq :inline :overlay-at (-> transform 0 trans x)))
|
||||
(:methods
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
|
||||
;; DECOMP BEGINS
|
||||
|
||||
;; Animation alignment turns motion authored on the skeleton's alignment joint
|
||||
;; into movement of the owning process. Each update samples the align joint and
|
||||
;; measures translation, scale, and rotation relative to the previous sample.
|
||||
;; The delta is disabled when the animation changes or crosses a
|
||||
;; loop boundary, where subtracting the two samples would create a false jump.
|
||||
;; Align-control moves a process through an animation.
|
||||
;; The "align" joint of the animation defines how the root of the process
|
||||
;; drawable should move.
|
||||
;; Each update samples the align joint and measures translation, scale, and
|
||||
;; rotation relative to the previous sample.
|
||||
;; matrix and transform use index 0 for the current sample and index 1 for the
|
||||
;; previous sample.
|
||||
|
||||
@@ -20,38 +20,40 @@
|
||||
the first frame of a non-looping animation so the discontinuity is not mistaken for root motion."
|
||||
(local-vars (disable-alignment? symbol))
|
||||
(with-pp
|
||||
;; Stack commands do not play their own frame group. Every other active channel must reference
|
||||
;; joint animation data before the alignment joint can be evaluated safely.
|
||||
;; Verify the joint animations are correct before any evaluation.
|
||||
(let ((active-channel-count (-> this process skel active-channels)))
|
||||
(dotimes (channel-index active-channel-count)
|
||||
(let* ((channel (-> this process skel channel channel-index))
|
||||
(channel-frame-group (-> channel frame-group))
|
||||
(channel-command (-> channel command))
|
||||
(stack-command 'stack)
|
||||
(stack-command? (= channel-command stack-command)))
|
||||
(cond
|
||||
((or stack-command? (begin (set! stack-command 'stack1) (= channel-command stack-command))))
|
||||
(channel-frame-group (-> channel frame-group)))
|
||||
;; og:preserve-this
|
||||
(case (-> channel command)
|
||||
(('stack 'stack1)
|
||||
;; stack commands don't introduce a new frame-group.
|
||||
)
|
||||
(else
|
||||
(when (!= (-> channel-frame-group type) art-joint-anim)
|
||||
;; og:preserve-this
|
||||
(go process-drawable-art-error "align joint-anim")
|
||||
(abandon-thread)
|
||||
0))))))
|
||||
(when (!= (-> channel-frame-group type) art-joint-anim)
|
||||
(go process-drawable-art-error "align joint-anim")
|
||||
(abandon-thread)))))))
|
||||
;; Disable alignment on frame-group switching, looping, or starting a new animation.
|
||||
(let* ((root-channel (-> this process skel root-channel 0))
|
||||
(current-frame-group (-> root-channel frame-group))
|
||||
(current-frame (-> root-channel frame-num)))
|
||||
(= (-> root-channel num-func) num-func-loop!)
|
||||
(cond
|
||||
;; switch frame-group
|
||||
((or (not current-frame-group) (!= (-> this frame-group) current-frame-group)) (set! disable-alignment? #t))
|
||||
;; loop wrap
|
||||
((= (-> root-channel num-func) num-func-loop!)
|
||||
(set! disable-alignment? (< (* (-> root-channel param 0) (- current-frame (-> this frame-num))) 0.0)))
|
||||
;; anim start
|
||||
(else (set! disable-alignment? (= current-frame 0.0))))
|
||||
(if disable-alignment? (logior! (-> this flags) (align-flags disabled)) (logclear! (-> this flags) (align-flags disabled)))
|
||||
(if disable-alignment?
|
||||
(logior! (-> this flags) (align-flags disabled))
|
||||
(logclear! (-> this flags) (align-flags disabled)))
|
||||
(set! (-> this frame-group) current-frame-group)
|
||||
(set! (-> this frame-num) current-frame))
|
||||
;; Shift the decomposed transform and source matrix into the previous-sample slots.
|
||||
(mem-copy! (the-as pointer (-> this transform 1)) (the-as pointer (-> this transform)) 48)
|
||||
(mem-copy! (the-as pointer (-> this transform 1)) (the-as pointer (-> this transform 0)) 48)
|
||||
(quaternion-copy! (-> this transform 1 quat) (-> this align quat))
|
||||
(vector-copy! (-> this transform 1 scale) (-> this align scale))
|
||||
(matrix-copy! (-> this matrix 1) (-> this matrix 0))
|
||||
@@ -64,7 +66,7 @@
|
||||
;; This "no-push" evaluation mode is a clever solution. A single group of animations are still interpolated,
|
||||
;; but cross-fades between animations (fade in/fade out) are not.
|
||||
;; For example, if there's a mix of run-left and run-straight, we'll interpolate the alignment.
|
||||
;; But, if were to fade into a "jump" by doing a (push jump), (stack) on top of the run,
|
||||
;; But, if were to fade into a "jump" by doing a (push jump), (stack) on top of the run,
|
||||
;; the "no-push" mode would make the jump animation override the run animation.
|
||||
;; this is only applied for the alignment matrix.
|
||||
(cspace<-matrix-no-push-joint! alignment-joint (-> this process skel))
|
||||
|
||||
@@ -6,30 +6,35 @@
|
||||
|
||||
;; DECOMP BEGINS
|
||||
|
||||
;; The combiner is the single camera transform consumed by rendering. During a camera change it
|
||||
;; blends the outgoing slave (slot 0) to the incoming slave (slot 1), then retires the outgoing
|
||||
;; process. Positions and field of view use the eased interpolation parameter; orientation uses the
|
||||
;; axis-angle construction described below so the basis stays orthonormal throughout the change.
|
||||
;;
|
||||
;; tracking-status selects where target-follow rotation is evaluated:
|
||||
;; 0 - use each slave's own rotation
|
||||
;; 1 - evaluate tracking once at the blended camera position
|
||||
;; 2 - evaluate tracking for the destination endpoint
|
||||
;; 3 - evaluate tracking for the source endpoint
|
||||
;; The endpoint-only modes bridge camera states with different tracking policies. Once the outgoing
|
||||
;; slave is removed, destination tracking becomes mode 1 and source tracking becomes mode 0.
|
||||
;; start-tracking adopts a slave's tracking controls and recalculates its follow direction at the
|
||||
;; slave position. copy-tracking additionally preserves the slave's current follow points and basis,
|
||||
;; which avoids a visible jump when tracking begins from an already active camera.
|
||||
;; Blend the active camera slaves into the single transform used for rendering.
|
||||
;; The complexity here is around camera orientation. Orientations are interpolated
|
||||
;; in a way that reduces motion sickness. The combiner can optionally recompute tracking,
|
||||
;; which helps aim the combined camera at the target.
|
||||
|
||||
;; Messages:
|
||||
;; (point-of-interest (pos [vector|#f]))
|
||||
;; Blend tracking toward pos, or disable point-of-interest tracking when pos is #f.
|
||||
;; (set-interpolation (duration time-frame))
|
||||
;; Restart the current slave blend and derive its update step from duration.
|
||||
;; (teleport)
|
||||
;; Recalculate combiner-owned tracking immediately after a discontinuous camera move.
|
||||
;; (stop-tracking)
|
||||
;; Stop calculating tracking in the combiner and use the slaves' rotations instead.
|
||||
;; (start-tracking (source camera-slave))
|
||||
;; Adopt source's tracking controls and calculate fresh follow state at its position.
|
||||
;; (copy-tracking (source camera-slave))
|
||||
;; Adopt source's tracking controls together with its current follow state and rotation,
|
||||
;; avoiding a discontinuity when an already tracking slave is handed to the combiner.
|
||||
(defstate cam-combiner-active (camera-combiner)
|
||||
:event
|
||||
(behavior ((proc process) (argc int) (message symbol) (block event-message-block))
|
||||
(local-vars (source-slave camera-slave))
|
||||
(case message
|
||||
(('point-of-interest)
|
||||
(cond
|
||||
((-> block param 0)
|
||||
(set! (-> self tracking use-point-of-interest) #t)
|
||||
(vector-copy! (-> self tracking point-of-interest) (the-as vector (-> block param 0)))
|
||||
(set! (-> self tracking point-of-interest quad) (-> (the-as vector (-> block param 0)) quad))
|
||||
(set! (-> self tracking point-of-interest-blend target) 1.0))
|
||||
(else (set! (-> self tracking use-point-of-interest) #f) (set! (-> self tracking point-of-interest-blend target) 0.0))))
|
||||
(('set-interpolation)
|
||||
@@ -38,215 +43,249 @@
|
||||
(('teleport)
|
||||
(when (nonzero? (-> self tracking-status))
|
||||
(cam-calc-follow! (-> self tracking) (-> self trans) #f)
|
||||
(slave-set-rotation! (-> self tracking) (-> self trans) (the-as float (-> self tracking-options)) (-> self fov) #f)))
|
||||
(('stop-tracking) (set! (-> self tracking-status) (the-as uint 0)) 0)
|
||||
(slave-set-rotation! (-> self tracking)
|
||||
(-> self trans)
|
||||
(the-as cam-slave-options (-> self tracking-options))
|
||||
(-> self fov)
|
||||
#f)))
|
||||
(('stop-tracking) (set! (-> self tracking-status) (cam-track-status use-slave-tracking)) 0)
|
||||
(('start-tracking)
|
||||
(cond
|
||||
((< argc 1)
|
||||
(let ((print-error-fn format)
|
||||
(output-stream 0)
|
||||
(error-message "ERROR <GMJ>: missing camera-slave parameter to *camera-combiner* start-tracking~%"))
|
||||
(let ((argument (-> block param 0))) (rtype-of argument))
|
||||
(print-error-fn output-stream error-message)))
|
||||
((let ((type-check-fn type-type?)
|
||||
(argument (-> block param 0)))
|
||||
(not (type-check-fn (rtype-of argument) camera-slave)))
|
||||
(let ((print-error-fn format)
|
||||
(output-stream 0)
|
||||
(error-message "ERROR <GMJ>: invalid type '~A' to *camera-combiner* start-tracking~%")
|
||||
(argument (-> block param 0)))
|
||||
(print-error-fn output-stream error-message (rtype-of argument))))
|
||||
((zero? (-> self tracking-status))
|
||||
(set! (-> self tracking-status) (the-as uint 1))
|
||||
(let ((source-slave (the-as camera-slave (-> block param 0))))
|
||||
(set! (-> self tracking-options) (the-as int (-> source-slave options)))
|
||||
(set! (-> self tracking no-follow) (-> source-slave tracking no-follow))
|
||||
(copy-cam-float-seeker (-> self tracking tilt-adjust) (-> source-slave tracking tilt-adjust))
|
||||
(copy-cam-float-seeker (-> self tracking underwater-blend) (-> source-slave tracking underwater-blend))
|
||||
(set! (-> self tracking use-point-of-interest) (-> source-slave tracking use-point-of-interest))
|
||||
(vector-copy! (-> self tracking point-of-interest) (-> source-slave tracking point-of-interest))
|
||||
(copy-cam-float-seeker (-> self tracking point-of-interest-blend) (-> source-slave tracking point-of-interest-blend))
|
||||
(let ((source-position (-> source-slave trans)))
|
||||
(cam-calc-follow! (-> self tracking) source-position #f)
|
||||
(slave-set-rotation! (-> self tracking) source-position (the-as float (-> self tracking-options)) (-> self fov) #f))))))
|
||||
(format 0
|
||||
"ERROR <GMJ>: missing camera-slave parameter to *camera-combiner* start-tracking~%"
|
||||
(rtype-of (-> block param 0))))
|
||||
((not (type-type? (rtype-of (-> block param 0)) camera-slave))
|
||||
(format 0 "ERROR <GMJ>: invalid type '~A' to *camera-combiner* start-tracking~%" (rtype-of (-> block param 0))))
|
||||
((= (-> self tracking-status) (cam-track-status use-slave-tracking))
|
||||
(set! (-> self tracking-status) (cam-track-status track-at-combiner))
|
||||
(set! source-slave (the-as camera-slave (-> block param 0)))
|
||||
(set! (-> self tracking-options) (the-as cam-slave-options-i (-> source-slave options)))
|
||||
(set! (-> self tracking no-follow) (-> source-slave tracking no-follow))
|
||||
(copy-cam-float-seeker (-> self tracking tilt-adjust) (-> source-slave tracking tilt-adjust))
|
||||
(copy-cam-float-seeker (-> self tracking underwater-blend) (-> source-slave tracking underwater-blend))
|
||||
(set! (-> self tracking use-point-of-interest) (-> source-slave tracking use-point-of-interest))
|
||||
(vector-copy! (-> self tracking point-of-interest) (-> source-slave tracking point-of-interest))
|
||||
(copy-cam-float-seeker (-> self tracking point-of-interest-blend) (-> source-slave tracking point-of-interest-blend))
|
||||
(let ((source-position (-> source-slave trans)))
|
||||
(cam-calc-follow! (-> self tracking) source-position #f)
|
||||
(slave-set-rotation! (-> self tracking)
|
||||
source-position
|
||||
(the-as cam-slave-options (-> self tracking-options))
|
||||
(-> self fov)
|
||||
#f)))))
|
||||
(('copy-tracking)
|
||||
(cond
|
||||
((< argc 1)
|
||||
(let ((print-error-fn format)
|
||||
(output-stream 0)
|
||||
(error-message "ERROR <GMJ>: missing camera-slave parameter to *camera-combiner* copy-tracking~%"))
|
||||
(let ((argument (-> block param 0))) (rtype-of argument))
|
||||
(print-error-fn output-stream error-message)))
|
||||
((let ((type-check-fn type-type?)
|
||||
(argument (-> block param 0)))
|
||||
(not (type-check-fn (rtype-of argument) camera-slave)))
|
||||
(let ((print-error-fn format)
|
||||
(output-stream 0)
|
||||
(error-message "ERROR <GMJ>: invalid type '~A' to *camera-combiner* copy-tracking~%")
|
||||
(argument (-> block param 0)))
|
||||
(print-error-fn output-stream error-message (rtype-of argument))))
|
||||
(format 0
|
||||
"ERROR <GMJ>: missing camera-slave parameter to *camera-combiner* copy-tracking~%"
|
||||
(rtype-of (-> block param 0))))
|
||||
((not (type-type? (rtype-of (-> block param 0)) camera-slave))
|
||||
(format 0 "ERROR <GMJ>: invalid type '~A' to *camera-combiner* copy-tracking~%" (rtype-of (-> block param 0))))
|
||||
((nonzero? (-> self tracking-status)) #f)
|
||||
(else
|
||||
(set! (-> self tracking-status) (the-as uint 1))
|
||||
(let ((source-slave (the-as camera-slave (-> block param 0))))
|
||||
(set! (-> self tracking-options) (the-as int (-> source-slave options)))
|
||||
(set! (-> self tracking no-follow) (-> source-slave tracking no-follow))
|
||||
(copy-cam-float-seeker (-> self tracking tilt-adjust) (-> source-slave tracking tilt-adjust))
|
||||
(copy-cam-float-seeker (-> self tracking underwater-blend) (-> source-slave tracking underwater-blend))
|
||||
(vector-copy! (-> self tracking follow-off) (-> source-slave tracking follow-off))
|
||||
(vector-copy! (-> self tracking follow-pt) (-> source-slave tracking follow-pt))
|
||||
(matrix-copy! (-> self tracking inv-mat) (-> source-slave tracking inv-mat))
|
||||
(set! (-> self tracking use-point-of-interest) (-> source-slave tracking use-point-of-interest))
|
||||
(vector-copy! (-> self tracking point-of-interest) (-> source-slave tracking point-of-interest))
|
||||
(copy-cam-float-seeker (-> self tracking point-of-interest-blend) (-> source-slave tracking point-of-interest-blend))))))))
|
||||
(set! (-> self tracking-status) (cam-track-status track-at-combiner))
|
||||
(set! source-slave (the-as camera-slave (-> block param 0)))
|
||||
(set! (-> self tracking-options) (the-as cam-slave-options-i (-> source-slave options)))
|
||||
(set! (-> self tracking no-follow) (-> source-slave tracking no-follow))
|
||||
(copy-cam-float-seeker (-> self tracking tilt-adjust) (-> source-slave tracking tilt-adjust))
|
||||
(copy-cam-float-seeker (-> self tracking underwater-blend) (-> source-slave tracking underwater-blend))
|
||||
(vector-copy! (-> self tracking follow-off) (-> source-slave tracking follow-off))
|
||||
(vector-copy! (-> self tracking follow-pt) (-> source-slave tracking follow-pt))
|
||||
(matrix-copy! (-> self tracking inv-mat) (-> source-slave tracking inv-mat))
|
||||
(set! (-> self tracking use-point-of-interest) (-> source-slave tracking use-point-of-interest))
|
||||
(vector-copy! (-> self tracking point-of-interest) (-> source-slave tracking point-of-interest))
|
||||
(copy-cam-float-seeker (-> self tracking point-of-interest-blend) (-> source-slave tracking point-of-interest-blend)))))))
|
||||
:code
|
||||
(behavior ()
|
||||
(local-vars (source-tracker cam-rotation-tracker))
|
||||
(local-vars (output-matrix matrix))
|
||||
;; tracking-status controls where target-follow rotation is evaluated:
|
||||
;; use-slave-tracking - use each slave's own rotation.
|
||||
;; track-at-combiner - evaluate once at the blended output position.
|
||||
;; track-at-dst - evaluate only the destination endpoint during a blend; this
|
||||
;; becomes track-at-combiner when the destination becomes active.
|
||||
;; track-at-src - evaluate only the source endpoint during a blend; this becomes
|
||||
;; use-slave-tracking when the source is retired.
|
||||
(loop
|
||||
(when (and (not (logtest? (-> *camera* master-options) 2)) (!= (-> self tracking-status) 0))
|
||||
(set! (-> self tracking-status) (the-as uint 0))
|
||||
;; Combiner tracking is meaningless once the master no longer has a target.
|
||||
(when (and (not (logtest? (-> *camera* master-options) (cam-master-options have-target)))
|
||||
(!= (-> self tracking-status) (cam-track-status use-slave-tracking)))
|
||||
(set! (-> self tracking-status) (cam-track-status use-slave-tracking))
|
||||
0)
|
||||
(when *camera*
|
||||
(let ((source-slave (-> *camera* slave 0))
|
||||
(destination-slave (-> *camera* slave 1))
|
||||
(blend (parameter-ease-sin-clamp (-> self interp-val)))
|
||||
(previous-position (new-stack-vector0)))
|
||||
(set! (-> previous-position quad) (-> self trans quad))
|
||||
(when source-slave
|
||||
(cond
|
||||
(destination-slave
|
||||
(vector-lerp-clamp! (-> self trans) (-> source-slave 0 trans) (-> destination-slave 0 trans) blend)
|
||||
(set! (-> self fov) (lerp-clamp (-> source-slave 0 fov) (-> destination-slave 0 fov) blend))
|
||||
(set! (-> self dist-from-src) (vector-vector-distance (-> self trans) (-> source-slave 0 trans)))
|
||||
(set! (-> self dist-from-dest) (vector-vector-distance (-> self trans) (-> destination-slave 0 trans)))
|
||||
(cond
|
||||
((= (-> self tracking-status) 1)
|
||||
(cam-calc-follow! (-> self tracking) (-> self trans) #t)
|
||||
(slave-set-rotation! (-> self tracking) (-> self trans) (the-as float (-> self tracking-options)) (-> self fov) #t)
|
||||
(matrix-copy! (-> self inv-camera-rot) (-> self tracking inv-mat)))
|
||||
(else
|
||||
(set! source-tracker (-> source-slave 0 tracking))
|
||||
(let ((source-position (-> source-slave 0 trans))
|
||||
(destination-tracker (-> destination-slave 0 tracking))
|
||||
(destination-position (-> destination-slave 0 trans)))
|
||||
(cond
|
||||
((= (-> self tracking-status) 3)
|
||||
(cam-calc-follow! (-> self tracking) source-position #t)
|
||||
(slave-set-rotation! (-> self tracking) source-position (the-as float (-> self tracking-options)) (-> self fov) #t)
|
||||
(set! source-tracker (-> self tracking))
|
||||
(set! source-position (-> self trans)))
|
||||
((= (-> self tracking-status) 2)
|
||||
(cam-calc-follow! (-> self tracking) destination-position #t)
|
||||
(slave-set-rotation! (-> self tracking) destination-position (the-as float (-> self tracking-options)) (-> self fov) #t)
|
||||
(set! destination-tracker (-> self tracking))
|
||||
(set! destination-position (-> self trans))))
|
||||
;; Any two orthonormal camera bases differ by one axis-angle rotation. The
|
||||
;; differences between corresponding basis rows lie perpendicular to that
|
||||
;; rotation axis. Cross the two strongest differences—the smallest is omitted
|
||||
;; because its source axis is closest to the rotation axis—to obtain a stable
|
||||
;; axis even when one row barely moves.
|
||||
(let ((rotation-work (new 'stack-no-clear 'matrix)))
|
||||
(dotimes (i 3)
|
||||
(set! (-> rotation-work vector i quad) (the-as uint128 0)))
|
||||
0.0
|
||||
0.0
|
||||
0.0
|
||||
(let ((rotation-axis (new-stack-vector0)))
|
||||
0.0
|
||||
(let ((rotation-matrix (new-stack-matrix0)))
|
||||
(vector-! (-> rotation-work vector 0)
|
||||
(the-as vector (-> source-tracker inv-mat))
|
||||
(the-as vector (-> destination-tracker inv-mat)))
|
||||
(vector-! (-> rotation-work vector 1) (-> source-tracker inv-mat vector 1) (-> destination-tracker inv-mat vector 1))
|
||||
(vector-! (-> rotation-work vector 2) (-> source-tracker inv-mat vector 2) (-> destination-tracker inv-mat vector 2))
|
||||
(let ((row-0-delta (vector-length (-> rotation-work vector 0)))
|
||||
(row-1-delta (vector-length (-> rotation-work vector 1)))
|
||||
(row-2-delta (vector-length (-> rotation-work vector 2))))
|
||||
(cond
|
||||
((and (< row-0-delta row-1-delta) (< row-0-delta row-2-delta))
|
||||
(vector-cross! rotation-axis (-> rotation-work vector 1) (-> rotation-work vector 2)))
|
||||
((and (< row-1-delta row-0-delta) (< row-1-delta row-2-delta))
|
||||
(vector-cross! rotation-axis (-> rotation-work vector 0) (-> rotation-work vector 2)))
|
||||
(else (vector-cross! rotation-axis (-> rotation-work vector 0) (-> rotation-work vector 1)))))
|
||||
(vector-normalize! rotation-axis 1.0)
|
||||
;; Project the source and destination row least parallel to the chosen
|
||||
;; axis into its perpendicular plane. Their angle is the relative camera
|
||||
;; rotation; their cross product fixes the sign of the axis.
|
||||
(let ((row-0-axis-dot (fabs (vector-dot (the-as vector (-> source-tracker inv-mat)) rotation-axis)))
|
||||
(row-1-axis-dot (fabs (vector-dot (-> source-tracker inv-mat vector 1) rotation-axis)))
|
||||
(row-2-axis-dot (fabs (vector-dot (-> source-tracker inv-mat vector 2) rotation-axis))))
|
||||
(cond
|
||||
((and (< row-0-axis-dot row-1-axis-dot) (< row-0-axis-dot row-2-axis-dot))
|
||||
(vector-flatten! (-> rotation-work vector 0) (the-as vector (-> source-tracker inv-mat)) rotation-axis)
|
||||
(vector-flatten! (-> rotation-work vector 1) (the-as vector (-> destination-tracker inv-mat)) rotation-axis))
|
||||
((< row-1-axis-dot row-2-axis-dot)
|
||||
(vector-flatten! (-> rotation-work vector 0) (-> source-tracker inv-mat vector 1) rotation-axis)
|
||||
(vector-flatten! (-> rotation-work vector 1) (-> destination-tracker inv-mat vector 1) rotation-axis))
|
||||
(else
|
||||
(vector-flatten! (-> rotation-work vector 0) (-> source-tracker inv-mat vector 2) rotation-axis)
|
||||
(vector-flatten! (-> rotation-work vector 1) (-> destination-tracker inv-mat vector 2) rotation-axis))))
|
||||
(vector-normalize! (-> rotation-work vector 0) 1.0)
|
||||
(vector-normalize! (-> rotation-work vector 1) 1.0)
|
||||
(vector-cross! (-> rotation-work vector 2) (-> rotation-work vector 0) (-> rotation-work vector 1))
|
||||
(if (< (vector-dot (-> rotation-work vector 2) rotation-axis) 0.0) (vector-negate! rotation-axis rotation-axis))
|
||||
(let ((rotation-angle (acos (vector-dot (-> rotation-work vector 0) (-> rotation-work vector 1)))))
|
||||
;; Bit 3 marks the first frame after adding a slave and bit 4 records
|
||||
;; use of the complementary rotation arc. Reset the old choice at the
|
||||
;; start of a transition. The target/destination geometry test ends in
|
||||
;; an empty conditional in this build and therefore does not choose an
|
||||
;; arc. On later frames, preserve the chosen axis across rotations over
|
||||
;; 90 degrees instead of letting its sign jump.
|
||||
(cond
|
||||
((logtest? (-> *camera* master-options) 8)
|
||||
(logand! (-> *camera* master-options) -25)
|
||||
(when (and (< 8192.0 rotation-angle) (logtest? (-> *camera* master-options) 2))
|
||||
(vector-! (-> rotation-work vector 0) (-> *camera* tpos-curr) source-position)
|
||||
(vector-! (-> rotation-work vector 1) destination-position source-position)
|
||||
(vector-flatten! (-> rotation-work vector 0) (-> rotation-work vector 0) (-> *camera* local-down))
|
||||
(vector-flatten! (-> rotation-work vector 1) (-> rotation-work vector 1) (-> *camera* local-down))
|
||||
(when (and (< 4096.0 (vector-normalize-ret-len! (-> rotation-work vector 0) 1.0))
|
||||
(< 4096.0 (vector-normalize-ret-len! (-> rotation-work vector 1) 1.0)))
|
||||
(vector-cross! (-> rotation-work vector 2) (-> rotation-work vector 1) (-> rotation-work vector 0))
|
||||
(when (< (vector-dot (-> rotation-work vector 2) rotation-axis) -0.01)))))
|
||||
((and (< 16384.0 rotation-angle) (< (vector-dot (-> self flip-control-axis) rotation-axis) 0.0))
|
||||
(logxor! (-> *camera* master-options) 16)))
|
||||
(set! (-> self flip-control-axis quad) (-> rotation-axis quad))
|
||||
(when (logtest? (-> *camera* master-options) 16)
|
||||
(set! rotation-angle (- 65536.0 rotation-angle))
|
||||
(vector-negate! rotation-axis rotation-axis))
|
||||
;; Apply the portion still separating the destination from the source:
|
||||
;; a full correction at blend 0 and the destination basis at blend 1.
|
||||
(let ((remaining-angle (* rotation-angle (- 1.0 blend))))
|
||||
(matrix-axis-sin-cos! rotation-matrix rotation-axis (sin remaining-angle) (cos remaining-angle))))
|
||||
(matrix*! (-> self inv-camera-rot) (-> destination-tracker inv-mat) rotation-matrix)))))))
|
||||
;; Hold interpolation while an outgoing camera curve is still reaching its exit
|
||||
;; point, and while the game is paused. This keeps the slave lifecycle aligned
|
||||
;; with the camera master's intro/outro curves.
|
||||
(cond
|
||||
((and (< 0.0 (-> *camera* outro-t-step)) (< (-> *camera* outro-t) (-> *camera* outro-exit-value))))
|
||||
((and (< (-> *camera* outro-t-step) 0.0) (< (-> *camera* outro-exit-value) (-> *camera* outro-t))))
|
||||
((paused?))
|
||||
(else (+! (-> self interp-val) (* (-> self interp-step) (-> *display* time-adjust-ratio)))))
|
||||
(when (>= (-> self interp-val) 1.0)
|
||||
(deactivate (-> *camera* slave 0 0))
|
||||
(set! (-> *camera* slave 0) (-> *camera* slave 1))
|
||||
(set! (-> *camera* slave 1) (the-as (pointer camera-slave) #f))
|
||||
(+! (-> *camera* num-slaves) -1)))
|
||||
(else
|
||||
(set! (-> self dist-from-src) 409600.0)
|
||||
(set! (-> self dist-from-dest) 0.0)
|
||||
(set! (-> self trans quad) (-> source-slave 0 trans quad))
|
||||
(set! (-> self fov) (-> source-slave 0 fov))
|
||||
(cond
|
||||
((= (-> self tracking-status) 2) (set! (-> self tracking-status) (the-as uint 1)))
|
||||
((= (-> self tracking-status) 3) (set! (-> self tracking-status) (the-as uint 0)) 0))
|
||||
(cond
|
||||
((= (-> self tracking-status) 1)
|
||||
(cam-calc-follow! (-> self tracking) (-> self trans) #t)
|
||||
(slave-set-rotation! (-> self tracking) (-> self trans) (the-as float (-> self tracking-options)) (-> self fov) #t)
|
||||
(matrix-copy! (-> self inv-camera-rot) (-> self tracking inv-mat)))
|
||||
(else
|
||||
(matrix-copy! (-> self inv-camera-rot) (-> source-slave 0 tracking inv-mat)))))))
|
||||
(vector-copy! previous-position (-> self trans))
|
||||
(if source-slave
|
||||
(set! output-matrix
|
||||
(cond
|
||||
(destination-slave
|
||||
;; Position and FOV can be interpolated directly. Rotation is blended as
|
||||
;; an axis-angle below so that the resulting camera basis stays orthonormal.
|
||||
(vector-lerp-clamp! (-> self trans) (-> source-slave 0 trans) (-> destination-slave 0 trans) blend)
|
||||
(set! (-> self fov) (lerp-clamp (-> source-slave 0 fov) (-> destination-slave 0 fov) blend))
|
||||
(set! (-> self dist-from-src) (vector-vector-distance (-> self trans) (-> source-slave 0 trans)))
|
||||
(set! (-> self dist-from-dest) (vector-vector-distance (-> self trans) (-> destination-slave 0 trans)))
|
||||
(set! output-matrix
|
||||
(cond
|
||||
((= (-> self tracking-status) (cam-track-status track-at-combiner))
|
||||
(cam-calc-follow! (-> self tracking) (-> self trans) #t)
|
||||
(slave-set-rotation! (-> self tracking)
|
||||
(-> self trans)
|
||||
(the-as cam-slave-options (-> self tracking-options))
|
||||
(-> self fov)
|
||||
#t)
|
||||
(matrix-copy! (-> self inv-camera-rot) (-> self tracking inv-mat)))
|
||||
(else
|
||||
(let ((source-tracker (-> source-slave 0 tracking))
|
||||
(source-position (-> source-slave 0 trans))
|
||||
(destination-tracker (-> destination-slave 0 tracking))
|
||||
(destination-position (-> destination-slave 0 trans)))
|
||||
(cond
|
||||
((= (-> self tracking-status) (cam-track-status track-at-src))
|
||||
(cam-calc-follow! (-> self tracking) source-position #t)
|
||||
(slave-set-rotation! (-> self tracking)
|
||||
source-position
|
||||
(the-as cam-slave-options (-> self tracking-options))
|
||||
(-> self fov)
|
||||
#t)
|
||||
(set! source-tracker (-> self tracking))
|
||||
(set! source-position (-> self trans)))
|
||||
((= (-> self tracking-status) (cam-track-status track-at-dst))
|
||||
(cam-calc-follow! (-> self tracking) destination-position #t)
|
||||
(slave-set-rotation! (-> self tracking)
|
||||
destination-position
|
||||
(the-as cam-slave-options (-> self tracking-options))
|
||||
(-> self fov)
|
||||
#t)
|
||||
(set! destination-tracker (-> self tracking))
|
||||
(set! destination-position (-> self trans))))
|
||||
;; Corresponding rows of two rotation bases differ in directions
|
||||
;; perpendicular to their shared axis of rotation. Cross the two
|
||||
;; strongest row deltas, omitting the row which changed least, to
|
||||
;; recover a stable rotation axis.
|
||||
(let ((rotation-work (new 'stack-no-clear 'matrix)))
|
||||
(dotimes (i 3)
|
||||
(set! (-> rotation-work vector i quad) (the-as uint128 0)))
|
||||
0.0
|
||||
0.0
|
||||
0.0
|
||||
(let ((rotation-axis (new-stack-vector0)))
|
||||
0.0
|
||||
(let ((rotation-matrix (new-stack-matrix0)))
|
||||
(vector-! (-> rotation-work vector 0)
|
||||
(the-as vector (-> source-tracker inv-mat))
|
||||
(the-as vector (-> destination-tracker inv-mat)))
|
||||
(vector-! (-> rotation-work vector 1) (-> source-tracker inv-mat vector 1) (-> destination-tracker inv-mat vector 1))
|
||||
(vector-! (-> rotation-work vector 2) (-> source-tracker inv-mat vector 2) (-> destination-tracker inv-mat vector 2))
|
||||
(let ((row-0-delta (vector-length (-> rotation-work vector 0)))
|
||||
(row-1-delta (vector-length (-> rotation-work vector 1)))
|
||||
(row-2-delta (vector-length (-> rotation-work vector 2))))
|
||||
(cond
|
||||
((and (< row-0-delta row-1-delta) (< row-0-delta row-2-delta))
|
||||
(vector-cross! rotation-axis (-> rotation-work vector 1) (-> rotation-work vector 2)))
|
||||
((and (< row-1-delta row-0-delta) (< row-1-delta row-2-delta))
|
||||
(vector-cross! rotation-axis (-> rotation-work vector 0) (-> rotation-work vector 2)))
|
||||
(else (vector-cross! rotation-axis (-> rotation-work vector 0) (-> rotation-work vector 1)))))
|
||||
(vector-normalize! rotation-axis 1.0)
|
||||
;; Choose the source basis row least parallel to the axis,
|
||||
;; then project that row and its destination counterpart into
|
||||
;; the plane normal to the axis. Their planar angle is the
|
||||
;; amount of rotation separating the two camera bases.
|
||||
(let ((row-0-axis-dot (fabs (vector-dot (the-as vector (-> source-tracker inv-mat)) rotation-axis)))
|
||||
(row-1-axis-dot (fabs (vector-dot (-> source-tracker inv-mat vector 1) rotation-axis)))
|
||||
(row-2-axis-dot (fabs (vector-dot (-> source-tracker inv-mat vector 2) rotation-axis))))
|
||||
(cond
|
||||
((and (< row-0-axis-dot row-1-axis-dot) (< row-0-axis-dot row-2-axis-dot))
|
||||
(vector-flatten! (-> rotation-work vector 0) (the-as vector (-> source-tracker inv-mat)) rotation-axis)
|
||||
(vector-flatten! (-> rotation-work vector 1) (the-as vector (-> destination-tracker inv-mat)) rotation-axis))
|
||||
((< row-1-axis-dot row-2-axis-dot)
|
||||
(vector-flatten! (-> rotation-work vector 0) (-> source-tracker inv-mat vector 1) rotation-axis)
|
||||
(vector-flatten! (-> rotation-work vector 1) (-> destination-tracker inv-mat vector 1) rotation-axis))
|
||||
(else
|
||||
(vector-flatten! (-> rotation-work vector 0) (-> source-tracker inv-mat vector 2) rotation-axis)
|
||||
(vector-flatten! (-> rotation-work vector 1) (-> destination-tracker inv-mat vector 2) rotation-axis))))
|
||||
(vector-normalize! (-> rotation-work vector 0) 1.0)
|
||||
(vector-normalize! (-> rotation-work vector 1) 1.0)
|
||||
(vector-cross! (-> rotation-work vector 2) (-> rotation-work vector 0) (-> rotation-work vector 1))
|
||||
;; Orient the otherwise sign-ambiguous axis to agree with the
|
||||
;; direction from the projected source row to destination row.
|
||||
(if (< (vector-dot (-> rotation-work vector 2) rotation-axis) 0.0) (vector-negate! rotation-axis rotation-axis))
|
||||
(let ((rotation-angle (acos (vector-dot (-> rotation-work vector 0) (-> rotation-work vector 1)))))
|
||||
(cond
|
||||
;; A newly activated slave starts a new arc decision, so
|
||||
;; discard both the one-shot marker and the previous flip.
|
||||
((logtest? (-> *camera* master-options) (cam-master-options set-combiner-axis))
|
||||
(logclear! (-> *camera* master-options) (cam-master-options set-combiner-axis flip-combiner))
|
||||
;; Compare the target and destination directions in the
|
||||
;; local horizontal plane. The final test has no body in
|
||||
;; this version and therefore has no observable effect.
|
||||
(when (and (< 8192.0 rotation-angle) (logtest? (-> *camera* master-options) (cam-master-options have-target)))
|
||||
(vector-! (-> rotation-work vector 0) (-> *camera* tpos-curr) source-position)
|
||||
(vector-! (-> rotation-work vector 1) destination-position source-position)
|
||||
(vector-flatten! (-> rotation-work vector 0) (-> rotation-work vector 0) (-> *camera* local-down))
|
||||
(vector-flatten! (-> rotation-work vector 1) (-> rotation-work vector 1) (-> *camera* local-down))
|
||||
(when (and (< 4096.0 (vector-normalize-ret-len! (-> rotation-work vector 0) 1.0))
|
||||
(< 4096.0 (vector-normalize-ret-len! (-> rotation-work vector 1) 1.0)))
|
||||
(vector-cross! (-> rotation-work vector 2) (-> rotation-work vector 1) (-> rotation-work vector 0))
|
||||
(when (< (vector-dot (-> rotation-work vector 2) rotation-axis) -0.01)))))
|
||||
;; For rotations over 90 degrees, keep the chosen axis
|
||||
;; continuous across frames by switching to the equivalent
|
||||
;; complementary axis-angle representation when necessary.
|
||||
((and (< 16384.0 rotation-angle) (< (vector-dot (-> self flip-control-axis) rotation-axis) 0.0))
|
||||
(logxor! (-> *camera* master-options) (cam-master-options flip-combiner))))
|
||||
(vector-copy! (-> self flip-control-axis) rotation-axis)
|
||||
(when (logtest? (-> *camera* master-options) (cam-master-options flip-combiner))
|
||||
(set! rotation-angle (- 65536.0 rotation-angle))
|
||||
(vector-negate! rotation-axis rotation-axis))
|
||||
;; Rotate backward from the destination basis by the portion
|
||||
;; still separating it from the source: the full angle at
|
||||
;; blend 0 and no correction at blend 1.
|
||||
(let ((remaining-angle (* rotation-angle (- 1.0 blend))))
|
||||
(matrix-axis-sin-cos! rotation-matrix rotation-axis (sin remaining-angle) (cos remaining-angle))))
|
||||
(matrix*! (-> self inv-camera-rot) (-> destination-tracker inv-mat) rotation-matrix)))))
|
||||
output-matrix)))
|
||||
;; Do not advance the blend while the outgoing slave is still reaching its
|
||||
;; authored exit point or while the game is paused.
|
||||
(cond
|
||||
((and (< 0.0 (-> *camera* outro-t-step)) (< (-> *camera* outro-t) (-> *camera* outro-exit-value))))
|
||||
((and (< (-> *camera* outro-t-step) 0.0) (< (-> *camera* outro-exit-value) (-> *camera* outro-t))))
|
||||
((paused?))
|
||||
(else (+! (-> self interp-val) (* (-> self interp-step) (-> *display* time-adjust-ratio)))))
|
||||
;; Once the blend reaches its destination, retire the outgoing slave and
|
||||
;; promote the destination into slot 0.
|
||||
(when (>= (-> self interp-val) 1.0)
|
||||
(deactivate (-> *camera* slave 0 0))
|
||||
(set! (-> *camera* slave 0) (-> *camera* slave 1))
|
||||
(set! (-> *camera* slave 1) (the-as (pointer camera-slave) #f))
|
||||
(+! (-> *camera* num-slaves) -1))
|
||||
output-matrix)
|
||||
(else
|
||||
(set! (-> self dist-from-src) 409600.0)
|
||||
(set! (-> self dist-from-dest) 0.0)
|
||||
(vector-copy! (-> self trans) (-> source-slave 0 trans))
|
||||
(set! (-> self fov) (-> source-slave 0 fov))
|
||||
;; Resolve the endpoint-only tracking modes after a transition has left just
|
||||
;; one slave: destination tracking becomes steady combiner tracking, while
|
||||
;; source tracking ends with the retired source.
|
||||
(cond
|
||||
((= (-> self tracking-status) (cam-track-status track-at-dst))
|
||||
(set! (-> self tracking-status) (cam-track-status track-at-combiner)))
|
||||
((= (-> self tracking-status) (cam-track-status track-at-src))
|
||||
(set! (-> self tracking-status) (cam-track-status use-slave-tracking))
|
||||
0))
|
||||
(cond
|
||||
((= (-> self tracking-status) (cam-track-status track-at-combiner))
|
||||
(cam-calc-follow! (-> self tracking) (-> self trans) #t)
|
||||
(slave-set-rotation! (-> self tracking)
|
||||
(-> self trans)
|
||||
(the-as cam-slave-options (-> self tracking-options))
|
||||
(-> self fov)
|
||||
#t)
|
||||
(matrix-copy! (-> self inv-camera-rot) (-> self tracking inv-mat)))
|
||||
(else (matrix-copy! (-> self inv-camera-rot) (-> source-slave 0 tracking inv-mat))))))))
|
||||
(vector-! (-> self velocity) (-> self trans) previous-position)))
|
||||
(if (and *dproc* *debug-segment*)
|
||||
(add-frame (-> *display* frames (-> *display* on-screen) frame profile-bar 0) 'camera (new 'static 'rgba :b #xff :a #x80)))
|
||||
@@ -262,7 +301,7 @@
|
||||
(if *math-camera* (set! (-> self fov) (-> *math-camera* fov)) (set! (-> self fov) 11650.845))
|
||||
(set! (-> self interp-val) 0.0)
|
||||
(set! (-> self interp-step) 0.125)
|
||||
(set! (-> self tracking-status) (the-as uint 0))
|
||||
(set! (-> self tracking-status) (cam-track-status use-slave-tracking))
|
||||
(vector-reset! (-> self velocity))
|
||||
(go cam-combiner-active)
|
||||
0
|
||||
|
||||
@@ -3,69 +3,42 @@
|
||||
(bundles "ENGINE.CGO" "GAME.CGO")
|
||||
(require "kernel-defs.gc")
|
||||
|
||||
;; TODO - for misty-obs
|
||||
(define-extern *camera-old-level* string) ;; unknown type
|
||||
;; Previous state for engine performance comparisons
|
||||
(define-extern *camera-old-level* string)
|
||||
(define-extern *camera-old-cpu* int)
|
||||
(define-extern *camera-old-vu* int)
|
||||
(define-extern *camera-old-tfrag-bytes* int)
|
||||
(define-extern *camera-old-stat-string-tfrag* string)
|
||||
(define-extern *camera-old-stat-string-tfrag-near* string)
|
||||
(define-extern *camera-old-stat-string-total* string)
|
||||
|
||||
(define-extern *camera-old-cpu* int) ;; unknown type
|
||||
|
||||
(define-extern *camera-old-vu* int) ;; unknown type
|
||||
|
||||
(define-extern *camera-old-tfrag-bytes* int) ;; unknown type
|
||||
|
||||
(define-extern *camera-old-stat-string-tfrag* string) ;; unknown type
|
||||
|
||||
(define-extern *camera-old-stat-string-tfrag-near* string) ;; unknown type
|
||||
|
||||
(define-extern *camera-old-stat-string-total* string) ;; unknown type
|
||||
|
||||
;; TODO - for cam-layout
|
||||
;; Debug drawing functions for camera
|
||||
(define-extern camera-line-setup (function vector4w none))
|
||||
|
||||
(define-extern camera-line-draw (function vector vector symbol))
|
||||
|
||||
(define-extern camera-line (function vector vector vector4w none))
|
||||
|
||||
(define-extern camera-cross (function vector vector vector vector4w meters basic))
|
||||
|
||||
(define-extern camera-fov-frame (function matrix vector float float float vector4w none))
|
||||
|
||||
(define-extern cam-slave-options->string (function cam-slave-options object string))
|
||||
|
||||
(define-extern cam-index-options->string (function cam-index-options object string))
|
||||
|
||||
(define-extern debug-set-camera-pos-rot! (function vector matrix vector))
|
||||
|
||||
(define-extern camera-slave-debug (function camera-slave none))
|
||||
|
||||
;; TODO - for cam-states
|
||||
(define-extern cam-debug-add-los-tri (function (inline-array collide-cache-tri) vector vector none))
|
||||
|
||||
(define-extern cam-collision-record-save (function vector vector int symbol camera-slave none))
|
||||
|
||||
(define-extern slave-los-state->string (function slave-los-state string))
|
||||
|
||||
(define-extern cam-debug-reset-coll-tri (function none)) ;; not confirmed
|
||||
|
||||
;; TODO - for rolling-lightning-mole
|
||||
(define-extern camera-line-rel (function vector vector vector4w none))
|
||||
|
||||
(declare-type clm basic)
|
||||
(define-extern cam-slave-options->string (function cam-slave-options object string))
|
||||
(define-extern cam-index-options->string (function cam-index-options object string))
|
||||
(define-extern debug-set-camera-pos-rot! (function vector matrix vector))
|
||||
(define-extern camera-slave-debug (function camera-slave none))
|
||||
(define-extern cam-debug-add-los-tri (function (inline-array collide-cache-tri) vector vector none))
|
||||
(define-extern cam-collision-record-save (function vector vector int symbol camera-slave none))
|
||||
(define-extern slave-los-state->string (function slave-los-state string))
|
||||
(define-extern cam-debug-reset-coll-tri (function none))
|
||||
|
||||
(define-extern *clm* clm) ;; unknown type
|
||||
|
||||
(define-extern *clm-edit* clm)
|
||||
|
||||
(define-extern *clm-focalpull-attr* clm) ;; unknown type
|
||||
|
||||
(define-extern *clm-index-attr* clm) ;; unknown type
|
||||
|
||||
(define-extern *clm-intro-attr* clm) ;; unknown type
|
||||
|
||||
(define-extern *clm-spline-attr* clm) ;; unknown type
|
||||
|
||||
(define-extern *clm-vol-attr* clm) ;; unknown type
|
||||
|
||||
(define-extern *clm-select* clm) ;; unknown type
|
||||
; (declare-type clm basic)
|
||||
; (define-extern *clm* clm)
|
||||
; (define-extern *clm-edit* clm)
|
||||
; (define-extern *clm-focalpull-attr* clm)
|
||||
; (define-extern *clm-index-attr* clm)
|
||||
; (define-extern *clm-intro-attr* clm)
|
||||
; (define-extern *clm-spline-attr* clm)
|
||||
; (define-extern *clm-vol-attr* clm)
|
||||
; (define-extern *clm-select* clm)
|
||||
|
||||
;; DECOMP BEGINS
|
||||
|
||||
|
||||
@@ -55,26 +55,6 @@
|
||||
:size-assert #x1d0
|
||||
:flag-assert #x9000001d0)
|
||||
|
||||
(defmethod inspect ((obj cam-dbg-scratch))
|
||||
(format #t "[~8x] ~A~%" obj 'cam-dbg-scratch)
|
||||
(format #t "~Tlinevec4w[2] @ #x~X~%" (-> obj linevec4w))
|
||||
(format #t "~Tcolor: ~`vector`P~%" (-> obj color))
|
||||
(format #t "~Tplotvec[2] @ #x~X~%" (-> obj plotvec))
|
||||
(format #t "~Tlinevec[2] @ #x~X~%" (-> obj linevec))
|
||||
(format #t "~Trel-vec: ~`vector`P~%" (-> obj rel-vec))
|
||||
(format #t "~Tsphere-v-start: ~`vector`P~%" (-> obj sphere-v-start))
|
||||
(format #t "~Tsphere-v-end: ~`vector`P~%" (-> obj sphere-v-end))
|
||||
(format #t "~Tsphere-v-down: ~`vector`P~%" (-> obj sphere-v-down))
|
||||
(format #t "~Tsphere-vec: ~`vector`P~%" (-> obj sphere-vec))
|
||||
(format #t "~Tcrossvec[3] @ #x~X~%" (-> obj crossvec))
|
||||
(format #t "~Tbboxvec[6] @ #x~X~%" (-> obj bboxvec))
|
||||
(format #t "~Tfov-vv[4] @ #x~X~%" (-> obj fov-vv))
|
||||
(format #t "~Tfov-src: ~`vector`P~%" (-> obj fov-src))
|
||||
(format #t "~Tfov-dest: ~`vector`P~%" (-> obj fov-dest))
|
||||
(format #t "~Tfov-vert: ~`vector`P~%" (-> obj fov-vert))
|
||||
(format #t "~Tfov-horz: ~`vector`P~%" (-> obj fov-horz))
|
||||
obj)
|
||||
|
||||
(defun cam-slave-options->string ((options cam-slave-options) (output object))
|
||||
"Append the names of the enabled camera-slave option bits to output and
|
||||
return output as a string."
|
||||
|
||||
Reference in New Issue
Block a user