mirror of
https://github.com/open-goal/jak-project
synced 2026-08-20 14:24:45 -04:00
1746 lines
98 KiB
Common Lisp
1746 lines
98 KiB
Common Lisp
;;-*-Lisp-*-
|
|
(in-package goal)
|
|
(bundles "ENGINE.CGO" "GAME.CGO")
|
|
(require "engine/util/glist-h.gc")
|
|
(require "engine/anim/aligner.gc")
|
|
|
|
;; The animation tester: an in-game browser for the animations of a loaded art
|
|
;; group. `anim-tester-add-object` loads one art group and adds an entry for its
|
|
;; mesh, holding every animation in that group. Any animation can be renamed into
|
|
;; a "sequence" and then edited into a playlist of items - animation, speed, blend
|
|
;; time, first and last frame - which the process plays end to end. Sequences are
|
|
;; written back out as text .obinf files.
|
|
;;
|
|
;; The debug menu drives all of it through events (see default-menu.gc), and the
|
|
;; four lists on screen are drawn by the generic list-control below, which calls
|
|
;; back into a per-list handler function for measuring, drawing and pad input.
|
|
|
|
;; NOTES - removed one of the 2 inspects
|
|
;; - also fixed up a basic file-stream constructor on the stack
|
|
|
|
(declare-type list-control structure)
|
|
|
|
(declare-type anim-tester process-drawable)
|
|
|
|
(define-extern anim-tester-save-all-objects (function anim-tester symbol))
|
|
|
|
(defenum anim-tester-flags
|
|
:bitfield #t
|
|
:type int32
|
|
(anim-playing) ;; an animation is set up on the skeleton, so post it
|
|
(just-entered) ;; entered anim-tester-process this frame; skip the menu once
|
|
(return-to-menu) ;; a list closed itself; hand control back to the debug menu
|
|
(editing-field) ;; pad input belongs to the highlighted field, not the list
|
|
(at-show-joint-info) ;; "Show Joint Inf" menu flag
|
|
(at-apply-align) ;; "Apply Align" menu flag
|
|
)
|
|
|
|
;; The command a list-control passes to its listfunc. display-list-control drives
|
|
;; the whole list through these five calls.
|
|
(defenum list-control-cmd
|
|
:type int32
|
|
(draw-line 0) ;; draw the current node at (xpos, ypos)
|
|
(visible? 1) ;; #t if the current node belongs in this list
|
|
(measure 2) ;; store the current node's width in characters in return-int
|
|
(draw-title 3) ;; draw the list title, and anything right of the list
|
|
(input 4) ;; consume this frame's pad input
|
|
)
|
|
|
|
(define-extern anim-test-anim-list-handler (function list-control-cmd list-control symbol))
|
|
|
|
(define-extern anim-test-edit-sequence-list-handler (function list-control-cmd list-control symbol))
|
|
|
|
;; Which list the tester is showing. The names are the events that select them.
|
|
(defenum anim-tester-edit-mode
|
|
:type int32
|
|
(none 0)
|
|
(pick-object 1)
|
|
(pick-joint-anim 2)
|
|
(pick-sequence 3)
|
|
(edit-sequence 4))
|
|
|
|
(defenum anim-test-obj-flags
|
|
:bitfield #t
|
|
:type int32
|
|
(edited) ;; sequences were changed; save-sequences will write the file
|
|
(play-sequence) ;; play seq-index out of the sequence list, not anim-index
|
|
)
|
|
|
|
(defenum anim-test-seq-flags
|
|
:bitfield #t
|
|
:type int32
|
|
(sequence) ;; a named sequence, not a plain animation from the art group
|
|
(anim-present) ;; the matching animation was found in the art group this scan
|
|
(from-file) ;; loaded from data/<object>.obinf, cleared when saved again
|
|
)
|
|
|
|
(defenum anim-test-item-flags
|
|
:bitfield #t
|
|
:type int32
|
|
(end) ;; the "**END**" row that closes every sequence
|
|
(wait-for-blend) ;; hold the sequence until this item's blend has finished
|
|
(blank) ;; "--blank--" row inserted at the end; not saved, not played
|
|
)
|
|
|
|
;; Columns of one sequence row, indexed by anim-tester's item-field. The three
|
|
;; flag columns after blend-flag are drawn as "-" and skipped when moving left or
|
|
;; right; the last three spell out the "MID" column header.
|
|
(defenum anim-test-item-field
|
|
:type int64
|
|
(name 0)
|
|
(speed 1)
|
|
(blend 2)
|
|
(first-frame 3)
|
|
(last-frame 4)
|
|
(blend-flag 5)
|
|
(unused-flag-1 6)
|
|
(unused-flag-2 7)
|
|
(unused-flag-3 8)
|
|
(move 9)
|
|
(insert 10)
|
|
(delete 11))
|
|
|
|
;; DECOMP BEGINS
|
|
|
|
;; this file is debug only
|
|
(declare-file (debug))
|
|
|
|
;; One scrolling list of glst-nodes on screen. Everything specific to a list
|
|
;; lives in its listfunc, which display-list-control calls once per node per
|
|
;; command; the fields below are how the two halves talk to each other.
|
|
(deftype list-control (structure)
|
|
((listfunc (function list-control-cmd list-control symbol))
|
|
(list-owner uint32) ;; whatever owns the list; the handler knows the type
|
|
(top int32) ;; panel position, in pixels
|
|
(left int32)
|
|
(list glst-list)
|
|
(the-node glst-node) ;; node the listfunc is being asked about
|
|
(top-index int32) ;; index of the first node on screen
|
|
(the-index int32) ;; index of the-node
|
|
(the-disp-line int32) ;; screen line the-node would be drawn on
|
|
(highlight-index int32) ;; index under the cursor
|
|
(current-index int32) ;; index the handler last accepted with X
|
|
(numlines int32) ;; visible nodes in the whole list
|
|
(lines-to-disp int32) ;; visible nodes on screen, at most MAX_LINES
|
|
(charswide int32) ;; width of the widest visible line, in characters
|
|
(highlight-disp-line int32) ;; screen line of the highlighted node
|
|
(field-id int32)
|
|
(xpos int32) ;; where the listfunc should draw, in pixels
|
|
(ypos int32)
|
|
(user-info int32) ;; per-list, set by whoever configured the control
|
|
(user-info-u uint32 :overlay-at user-info)
|
|
(return-int int32)) ;; the listfunc's answer to a measure command
|
|
:allow-misaligned)
|
|
|
|
;; Column of a sequence-editor row, in characters from the left of the panel.
|
|
(deftype list-field (structure)
|
|
((left int32)
|
|
(width int32)))
|
|
|
|
;; Layout constants shared by every list-control. Sizes are in pixels except
|
|
;; MAX_LINES and BORDER_LINES, which are screen lines, and CHAR_WIDTH, which is
|
|
;; how wide the code assumes one character of the debug font is.
|
|
(deftype DISP_LIST-bank (basic)
|
|
((TV_SPACING int32)
|
|
(BORDER_WIDTH int32)
|
|
(BORDER_HEIGHT int32)
|
|
(MAX_LINES int32)
|
|
(CHAR_WIDTH int32)
|
|
(INC_DELAY int32)
|
|
(BORDER_LINES int32)
|
|
(CXOFF int32) ;; the four offsets are left at zero
|
|
(CYOFF int32)
|
|
(BXOFF int32)
|
|
(BYOFF int32)))
|
|
|
|
(define *DISP_LIST-bank*
|
|
(new 'static
|
|
'DISP_LIST-bank
|
|
:TV_SPACING 8
|
|
:BORDER_WIDTH 4
|
|
:BORDER_HEIGHT 4
|
|
:MAX_LINES 12
|
|
:CHAR_WIDTH 10
|
|
:INC_DELAY 20
|
|
:BORDER_LINES 3))
|
|
|
|
(defun display-list-control ((ctrl list-control))
|
|
"Draw one list and give it this frame's pad input.
|
|
|
|
Both indices are clamped into range first, then the list is walked with
|
|
(list-control-cmd visible?) so a handler can filter nodes out: if the
|
|
highlighted node is filtered out, the highlight moves to the first visible
|
|
node. The visible nodes are measured to size the panel, the panel and title
|
|
are drawn, and top-index is moved until the highlight sits at least
|
|
BORDER_LINES from either edge of the window. Finally up to MAX_LINES rows are
|
|
drawn, or \"**NONE**\" if nothing is visible.
|
|
|
|
the-node, the-index and the-disp-line are what a handler reads to find out
|
|
which node it is being asked about."
|
|
(when (< (-> ctrl current-index) 0)
|
|
(set! (-> ctrl current-index) 0)
|
|
0)
|
|
;; -1 means nothing in this list is selected any more
|
|
(if (>= (-> ctrl current-index) (glst-num-elements (-> ctrl list))) (set! (-> ctrl current-index) -1))
|
|
(when (< (-> ctrl highlight-index) 0)
|
|
(set! (-> ctrl highlight-index) 0)
|
|
0)
|
|
(if (>= (-> ctrl highlight-index) (glst-num-elements (-> ctrl list)))
|
|
(set! (-> ctrl highlight-index) (+ (glst-num-elements (-> ctrl list)) -1)))
|
|
(set! (-> ctrl the-index) (-> ctrl highlight-index))
|
|
(set! (-> ctrl the-node) (glst-get-node-by-index (-> ctrl list) (-> ctrl highlight-index)))
|
|
;; the highlighted node may be one the handler wants hidden. If so, highlight the
|
|
;; first visible node instead, or fall back to 0 if the whole list is hidden.
|
|
(when (not ((-> ctrl listfunc) (list-control-cmd visible?) ctrl))
|
|
(set! (-> ctrl the-index) 0)
|
|
(let ((list (-> ctrl list))) "return the start of the list" (set! (-> ctrl the-node) (-> list head)))
|
|
(while (let ((node (-> ctrl the-node))) "is this node the end of the list. #t = end" (not (not (-> node next))))
|
|
(when ((-> ctrl listfunc) (list-control-cmd visible?) ctrl)
|
|
(set! (-> ctrl highlight-index) (-> ctrl the-index))
|
|
(goto cfg-18))
|
|
(+! (-> ctrl the-index) 1)
|
|
(let ((node (-> ctrl the-node))) "return the next node in the list" (set! (-> ctrl the-node) (-> node next))))
|
|
(set! (-> ctrl highlight-index) 0)
|
|
0)
|
|
(label cfg-18)
|
|
(set! (-> ctrl the-index) (-> ctrl highlight-index))
|
|
(set! (-> ctrl the-node) (glst-get-node-by-index (-> ctrl list) (-> ctrl the-index)))
|
|
((-> ctrl listfunc) (list-control-cmd input) ctrl)
|
|
;; measure the list: count the visible nodes, find the widest one, and work out
|
|
;; which screen line the highlight lands on with the current top-index
|
|
(let ((past-top? #f)
|
|
(found-highlight? #f))
|
|
(set! (-> ctrl numlines) 0)
|
|
(set! (-> ctrl charswide) 0)
|
|
(set! (-> ctrl the-index) 0)
|
|
(set! (-> ctrl the-disp-line) 0)
|
|
(let ((list (-> ctrl list))) "return the start of the list" (set! (-> ctrl the-node) (-> list head)))
|
|
(while (let ((node (-> ctrl the-node))) "is this node the end of the list. #t = end" (not (not (-> node next))))
|
|
(when ((-> ctrl listfunc) (list-control-cmd visible?) ctrl)
|
|
(if (and (not past-top?) (>= (-> ctrl the-index) (-> ctrl top-index))) (set! past-top? #t))
|
|
(when (and (not found-highlight?) (>= (-> ctrl the-index) (-> ctrl highlight-index)))
|
|
(set! found-highlight? #t)
|
|
(set! (-> ctrl highlight-disp-line) (-> ctrl the-disp-line)))
|
|
((-> ctrl listfunc) (list-control-cmd measure) ctrl)
|
|
(if (< (-> ctrl charswide) (-> ctrl return-int)) (set! (-> ctrl charswide) (-> ctrl return-int)))
|
|
(if past-top? (+! (-> ctrl the-disp-line) 1))
|
|
(+! (-> ctrl numlines) 1))
|
|
(+! (-> ctrl the-index) 1)
|
|
(let ((node (-> ctrl the-node))) "return the next node in the list" (set! (-> ctrl the-node) (-> node next)))))
|
|
(set! (-> ctrl lines-to-disp)
|
|
(if (< (-> *DISP_LIST-bank* MAX_LINES) (-> ctrl numlines)) (-> *DISP_LIST-bank* MAX_LINES) (-> ctrl numlines)))
|
|
;; result unused
|
|
(if (> (-> ctrl lines-to-disp) 0) (-> ctrl lines-to-disp) 1)
|
|
(with-dma-buffer-add-bucket ((dma-buff (-> *display* frames (-> *display* on-screen) frame debug-buf)) (bucket-id debug)) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (draw-sprite2d-xy dma-buff
|
|
(-> ctrl left)
|
|
(-> ctrl top)
|
|
(+ (* (-> ctrl charswide) (-> *DISP_LIST-bank* CHAR_WIDTH)) (* (-> *DISP_LIST-bank* BORDER_WIDTH) 2))
|
|
(+ (* (+ (-> ctrl lines-to-disp) 1) (-> *DISP_LIST-bank* TV_SPACING)) (* (-> *DISP_LIST-bank* BORDER_WIDTH) 2))
|
|
(new 'static 'rgba :a #x40)))
|
|
(set! (-> ctrl xpos) (+ (-> ctrl left) (-> *DISP_LIST-bank* BORDER_WIDTH)))
|
|
(set! (-> ctrl ypos) (+ (-> ctrl top) (-> *DISP_LIST-bank* BORDER_HEIGHT)))
|
|
((-> ctrl listfunc) (list-control-cmd draw-title) ctrl)
|
|
(cond
|
|
((> (-> ctrl lines-to-disp) 0)
|
|
(cond
|
|
;; the highlight is too close to the top of the window: walk top-index back
|
|
;; over visible nodes until it is BORDER_LINES down, or the list runs out
|
|
((< (-> ctrl highlight-disp-line) (-> *DISP_LIST-bank* BORDER_LINES))
|
|
(let ((lines-to-scroll (- (-> *DISP_LIST-bank* BORDER_LINES) (-> ctrl highlight-disp-line))))
|
|
(set! (-> ctrl the-node) (glst-get-node-by-index (-> ctrl list) (-> ctrl top-index)))
|
|
(set! (-> ctrl the-index) (-> ctrl top-index))
|
|
(let ((node (-> ctrl the-node)))
|
|
"is this node the start of the list. #t = start"
|
|
(when (not (not (-> node prev)))
|
|
(loop
|
|
(let ((cur-node (-> ctrl the-node)))
|
|
"return the previous node in the list"
|
|
(let ((prev-node (-> cur-node prev)))
|
|
(let ((prev prev-node)) "is this node the start of the list. #t = start" (if (not (-> prev prev)) (goto cfg-61)))
|
|
(set! (-> ctrl the-node) prev-node)))
|
|
(+! (-> ctrl the-index) -1)
|
|
(when ((-> ctrl listfunc) (list-control-cmd visible?) ctrl)
|
|
(set! (-> ctrl top-index) (-> ctrl the-index))
|
|
(+! lines-to-scroll -1)
|
|
(if (<= lines-to-scroll 0) (goto cfg-61)))))))
|
|
(label cfg-61))
|
|
;; and the same the other way, for the bottom of the window
|
|
((>= (-> ctrl highlight-disp-line) (- (-> *DISP_LIST-bank* MAX_LINES) (-> *DISP_LIST-bank* BORDER_LINES)))
|
|
(let ((lines-to-scroll (- (-> ctrl highlight-disp-line) (- (-> *DISP_LIST-bank* MAX_LINES) (-> *DISP_LIST-bank* BORDER_LINES)))))
|
|
(set! (-> ctrl the-node) (glst-get-node-by-index (-> ctrl list) (-> ctrl top-index)))
|
|
(set! (-> ctrl the-index) (-> ctrl top-index))
|
|
(let ((node (-> ctrl the-node)))
|
|
"is this node the end of the list. #t = end"
|
|
(when (not (not (-> node next)))
|
|
(loop
|
|
(let ((cur-node (-> ctrl the-node)))
|
|
"return the next node in the list"
|
|
(let ((next-node (-> cur-node next)))
|
|
(let ((next next-node)) "is this node the end of the list. #t = end" (if (not (-> next next)) (goto cfg-77)))
|
|
(set! (-> ctrl the-node) next-node)))
|
|
(+! (-> ctrl the-index) 1)
|
|
(when ((-> ctrl listfunc) (list-control-cmd visible?) ctrl)
|
|
(set! (-> ctrl top-index) (-> ctrl the-index))
|
|
(+! lines-to-scroll -1)
|
|
(if (<= lines-to-scroll 0) (goto cfg-77)))))))))
|
|
(label cfg-77)
|
|
;; draw from top-index until the window is full or the list ends
|
|
(set! (-> ctrl the-disp-line) 0)
|
|
(set! (-> ctrl the-index) (-> ctrl top-index))
|
|
(set! (-> ctrl the-node) (glst-get-node-by-index (-> ctrl list) (-> ctrl top-index)))
|
|
(while (let ((node (-> ctrl the-node)))
|
|
"is this node the end of the list. #t = end"
|
|
(not (or (not (-> node next)) (>= (-> ctrl the-disp-line) (-> *DISP_LIST-bank* MAX_LINES)))))
|
|
(when ((-> ctrl listfunc) (list-control-cmd visible?) ctrl)
|
|
(set! (-> ctrl xpos) (+ (-> ctrl left) (-> *DISP_LIST-bank* BORDER_WIDTH)))
|
|
(set! (-> ctrl ypos)
|
|
(+ (-> ctrl top) (-> *DISP_LIST-bank* BORDER_HEIGHT) (* (+ (-> ctrl the-disp-line) 1) (-> *DISP_LIST-bank* TV_SPACING))))
|
|
((-> ctrl listfunc) (list-control-cmd draw-line) ctrl)
|
|
(+! (-> ctrl the-disp-line) 1))
|
|
(+! (-> ctrl the-index) 1)
|
|
(let ((node (-> ctrl the-node))) "return the next node in the list" (set! (-> ctrl the-node) (-> node next)))))
|
|
(else
|
|
(with-dma-buffer-add-bucket ((dma-buff (-> *display* frames (-> *display* on-screen) frame debug-buf)) (bucket-id debug)) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (draw-string-xy "**NONE**"
|
|
dma-buff
|
|
(+ (-> ctrl left) (-> *DISP_LIST-bank* BORDER_WIDTH))
|
|
(+ (-> ctrl top) (-> *DISP_LIST-bank* BORDER_HEIGHT) (-> *DISP_LIST-bank* TV_SPACING))
|
|
(font-color menu)
|
|
(font-flags shadow kerning)))))
|
|
(none))
|
|
|
|
;; Where each of the four lists sits, and how narrow it is allowed to get. X, Y
|
|
;; and EDIT_STATS_X/EDIT_PICK_X are pixels and characters respectively; the widths
|
|
;; are in characters.
|
|
(deftype anim-tester-bank (basic)
|
|
((ANIM_SPEED float) ;; unused
|
|
(BLEND float) ;; unused
|
|
(OBJECT_LIST_X int32)
|
|
(OBJECT_LIST_Y int32)
|
|
(OBJECT_LIST_MIN_WIDTH int32)
|
|
(ANIM_LIST_X int32)
|
|
(ANIM_LIST_Y int32)
|
|
(ANIM_LIST_MIN_WIDTH int32)
|
|
(PICK_LIST_X int32)
|
|
(PICK_LIST_Y int32)
|
|
(PICK_LIST_MIN_WIDTH int32)
|
|
(EDIT_LIST_X int32)
|
|
(EDIT_LIST_Y int32)
|
|
(EDIT_STATS_X int32)
|
|
(EDIT_LIST_MIN_WIDTH int32)
|
|
(EDIT_PICK_X int32)))
|
|
|
|
(define *ANIM_TESTER-bank*
|
|
(new 'static
|
|
'anim-tester-bank
|
|
:ANIM_SPEED 1.0
|
|
:BLEND 1.0
|
|
:OBJECT_LIST_X 10
|
|
:OBJECT_LIST_Y 50
|
|
:OBJECT_LIST_MIN_WIDTH 18
|
|
:ANIM_LIST_X 10
|
|
:ANIM_LIST_Y 50
|
|
:ANIM_LIST_MIN_WIDTH 17
|
|
:PICK_LIST_X 10
|
|
:PICK_LIST_Y 50
|
|
:PICK_LIST_MIN_WIDTH 21
|
|
:EDIT_LIST_X 10
|
|
:EDIT_LIST_Y 50
|
|
:EDIT_STATS_X 30
|
|
:EDIT_LIST_MIN_WIDTH 64
|
|
:EDIT_PICK_X 30))
|
|
|
|
;; The tester process itself. It draws one loaded mesh with one animation channel
|
|
;; set, and owns the object list and the animation picker; the per-object and
|
|
;; per-sequence lists live on the nodes they belong to.
|
|
(deftype anim-tester (process-drawable)
|
|
((flags anim-tester-flags)
|
|
(obj-list glst-list :inline) ;; every loaded art group, as anim-test-obj
|
|
(current-obj string) ;; name of the object being played
|
|
(speed int32) ;; global playback speed, percent
|
|
(list-con list-control :inline) ;; the object list
|
|
(pick-con list-control :inline) ;; the animation picker inside the editor
|
|
(item-field int64) ;; anim-test-item-field: editor column
|
|
(inc-delay int32) ;; frames between steps while a value is held
|
|
(inc-timer int32) ;; frames left before the next step
|
|
(edit-mode anim-tester-edit-mode)
|
|
(old-mode anim-tester-edit-mode) ;; edit-mode last frame; written, never read
|
|
(anim-speed float) ;; frames per frame for the current row
|
|
(anim-gspeed float) ;; speed as a fraction, without the row's own
|
|
(anim-first float) ;; frame limits for the current row, or the
|
|
(anim-last float)) ;; min/max sentinels
|
|
(:states
|
|
anim-tester-process))
|
|
|
|
(defun anim-tester-num-print ((stream basic) (frame float))
|
|
"Print a frame number to stream for an .obinf file, spelling the two sentinels
|
|
as \"min\" (-1.0) and \"max\" (-2.0)."
|
|
(cond
|
|
((= frame -2.0) (format stream "max"))
|
|
((= frame -1.0) (format stream "min"))
|
|
(else (format stream "~f" frame)))
|
|
(none))
|
|
|
|
(define-perm *anim-tester* (pointer anim-tester) #f)
|
|
|
|
;; One mesh out of one art group, and everything the tester knows about its
|
|
;; animations. privname is the mesh's name, which is what the object list shows.
|
|
(deftype anim-test-obj (glst-named-node)
|
|
((obj-art-group art-group)
|
|
(seq-list glst-list :inline) ;; anim-test-sequence, one per animation
|
|
(flags anim-test-obj-flags)
|
|
(mesh-geo merc-ctrl)
|
|
(joint-geo art-joint-geo)
|
|
(list-con list-control :inline) ;; the animation list, and the sequence list
|
|
(parent uint32) ;; see anim-test-obj-init; never read
|
|
(anim-index int32) ;; selection and cursor kept across a close,
|
|
(anim-hindex int32) ;; for the animation list
|
|
(seq-index int32) ;; and for the sequence list
|
|
(seq-hindex int32))
|
|
(:methods
|
|
(new (symbol type int string basic) _type_)))
|
|
|
|
(defun anim-test-obj-init ((obj anim-test-obj) (parent-ctrl list-control))
|
|
"Point obj's animation list at its own sequence list and place it on screen.
|
|
parent-ctrl is meant to supply the node that owns obj, but every caller passes
|
|
the anim-tester process itself, so parent ends up holding a word out of the
|
|
process header. Nothing reads it."
|
|
(set! (-> obj mesh-geo) #f)
|
|
(set! (-> obj joint-geo) #f)
|
|
(set! (-> obj list-con listfunc) anim-test-anim-list-handler)
|
|
(set! (-> obj list-con left) (-> *ANIM_TESTER-bank* ANIM_LIST_X))
|
|
(set! (-> obj list-con top) (-> *ANIM_TESTER-bank* ANIM_LIST_Y))
|
|
(set! (-> obj list-con list) (-> obj seq-list))
|
|
(set! (-> obj list-con list-owner) (the-as uint obj))
|
|
(let ((ctrl parent-ctrl)) (set! (-> obj parent) (the-as uint (if ctrl (-> ctrl the-node)))))
|
|
(none))
|
|
|
|
(defmethod new anim-test-obj ((allocation symbol) (type-to-make type) (count int) (name string) (ag basic))
|
|
"Allocate a browser entry for the mesh named name in art group ag, with an
|
|
empty sequence list. count is ignored; every caller passes 1."
|
|
(let ((struct-new (method-of-type structure new))
|
|
(alloc-type type-to-make))
|
|
(-> type-to-make size)
|
|
(let ((obj (the-as anim-test-obj (struct-new allocation alloc-type))))
|
|
(set! (-> obj obj-art-group) (the-as art-group ag))
|
|
(set! (-> obj privname) name)
|
|
(glst-init-list! (-> obj seq-list))
|
|
obj)))
|
|
|
|
;; One entry of an object's animation list. Every animation in the art group gets
|
|
;; one of these holding a single item; naming or editing it turns it into a
|
|
;; sequence, which is a playlist of items played end to end.
|
|
;;
|
|
;; Note that parent lands exactly where list-con's user-info sits in an
|
|
;; anim-test-obj, which is how the shared animation-list handler finds the object
|
|
;; a picked animation belongs to.
|
|
(deftype anim-test-sequence (glst-named-node)
|
|
((item-list glst-list :inline) ;; anim-test-seq-item, ending in an **END** row
|
|
(playing-item int32) ;; index of the row being played
|
|
(flags anim-test-seq-flags)
|
|
(list-con list-control :inline) ;; the sequence editor
|
|
(parent anim-test-obj))
|
|
(:methods
|
|
(new (symbol type int string) _type_)))
|
|
|
|
(defun anim-test-sequence-init ((seq anim-test-sequence) (obj anim-test-obj))
|
|
"Point seq's editor at its own item list, place it on screen and remember the
|
|
object it belongs to."
|
|
(set! (-> seq list-con listfunc) anim-test-edit-sequence-list-handler)
|
|
(set! (-> seq list-con left) (-> *ANIM_TESTER-bank* EDIT_LIST_X))
|
|
(set! (-> seq list-con top) (-> *ANIM_TESTER-bank* EDIT_LIST_Y))
|
|
(set! (-> seq list-con list) (-> seq item-list))
|
|
(set! (-> seq list-con list-owner) (the-as uint seq))
|
|
(set! (-> seq parent) obj)
|
|
(none))
|
|
|
|
(defmethod new anim-test-sequence ((allocation symbol) (type-to-make type) (count int) (name string))
|
|
"Allocate a sequence named name with an empty item list. count is ignored."
|
|
(let ((struct-new (method-of-type structure new))
|
|
(alloc-type type-to-make))
|
|
(-> type-to-make size)
|
|
(let ((seq (the-as anim-test-sequence (struct-new allocation alloc-type))))
|
|
(set! (-> seq privname) name)
|
|
(glst-init-list! (-> seq item-list))
|
|
seq)))
|
|
|
|
;; One row of a sequence: play animation privname, at speed, blending in over
|
|
;; blend frames, from first-frame to last-frame.
|
|
(deftype anim-test-seq-item (glst-named-node)
|
|
((speed int32) ;; percent of the tester's speed; negative plays backwards
|
|
(blend int32) ;; blend length in frames, before the global speed
|
|
(first-frame float) ;; -1.0 means min, -2.0 means max
|
|
(last-frame float)
|
|
(num-frames float) ;; length of the animation, refreshed from the art group
|
|
(artist-base float) ;; frame number the animator's first frame had
|
|
(flags anim-test-item-flags)
|
|
(parent anim-test-sequence))
|
|
(:methods
|
|
(new (symbol type int string) _type_)))
|
|
|
|
(defmethod new anim-test-seq-item ((allocation symbol) (type-to-make type) (count int) (name string))
|
|
"Allocate a row that plays animation name at full speed with no blend, from
|
|
its first frame (min) to its last (max). count is ignored."
|
|
(let ((struct-new (method-of-type structure new))
|
|
(alloc-type type-to-make))
|
|
(-> type-to-make size)
|
|
(let ((item (the-as anim-test-seq-item (struct-new allocation alloc-type))))
|
|
(set! (-> item privname) name)
|
|
(set! (-> item speed) 100)
|
|
(set! (-> item blend) 0)
|
|
(set! (-> item first-frame) -1.0)
|
|
(set! (-> item last-frame) -2.0)
|
|
item)))
|
|
|
|
(defun anim-test-seq-item-copy! ((dst anim-test-seq-item) (src anim-test-seq-item))
|
|
"Copy src onto dst, name and owning sequence included. Returns src's sequence."
|
|
(let ((node dst)) (set! (-> node privname) (-> src privname)))
|
|
(set! (-> dst speed) (-> src speed))
|
|
(set! (-> dst blend) (-> src blend))
|
|
(set! (-> dst first-frame) (-> src first-frame))
|
|
(set! (-> dst last-frame) (-> src last-frame))
|
|
(set! (-> dst num-frames) (-> src num-frames))
|
|
(set! (-> dst artist-base) (-> src artist-base))
|
|
(set! (-> dst flags) (-> src flags))
|
|
(let ((parent-seq (-> src parent))) (set! (-> dst parent) parent-seq) parent-seq))
|
|
|
|
(defun anim-test-obj-item-valid? ((obj anim-test-obj) (item anim-test-seq-item))
|
|
"Can item still be played? True when one of obj's sequences has the same name
|
|
and was found in the art group during the last scan. As a side effect item's
|
|
num-frames and artist-base are refreshed from that sequence's own first item,
|
|
so a row's frame limits follow a reloaded animation. Returns #f otherwise."
|
|
(let ((seq-list (-> obj seq-list)))
|
|
"return the start of the list"
|
|
(let ((cur-seq (the-as anim-test-sequence (-> seq-list head))))
|
|
(while (let ((sequence-node cur-seq)) "is this node the end of the list. #t = end" (not (not (-> sequence-node next))))
|
|
(when (and (logtest? (-> cur-seq flags) (anim-test-seq-flags anim-present)) (name= (-> item privname) (-> cur-seq privname)))
|
|
(let ((item-list (-> cur-seq item-list)))
|
|
"return the start of the list"
|
|
(let* ((first-item (the-as anim-test-seq-item (-> item-list head)))
|
|
(item-node first-item))
|
|
"is this node the end of the list. #t = end"
|
|
(when (not (not (-> item-node next)))
|
|
(set! (-> item num-frames) (-> first-item num-frames))
|
|
(set! (-> item artist-base) (-> first-item artist-base)))))
|
|
(return #t))
|
|
"return the next node in the list"
|
|
(set! cur-seq (the-as anim-test-sequence (-> cur-seq next))))))
|
|
#f)
|
|
|
|
(defun anim-test-obj-remove-invalid ((obj anim-test-obj))
|
|
"Throw away everything in obj that the art group no longer supports: rows whose
|
|
animation has gone, and then any sequence left holding nothing but its **END**
|
|
row. Clears anim-present on every surviving sequence so the next scan of the
|
|
art group can set it again. Always returns #f."
|
|
(local-vars (only-end-item? symbol))
|
|
(let ((seq-list (-> obj seq-list)))
|
|
"return the start of the list"
|
|
(let ((cur-seq (the-as anim-test-sequence (-> seq-list head))))
|
|
(while (let ((sequence-node cur-seq)) "is this node the end of the list. #t = end" (not (not (-> sequence-node next))))
|
|
(let ((cur-node cur-seq))
|
|
"return the next node in the list"
|
|
(let ((next-seq (-> cur-node next)))
|
|
(when (not (logtest? (-> cur-seq flags) (anim-test-seq-flags anim-present)))
|
|
(let ((item-list (-> cur-seq item-list)))
|
|
"return the start of the list"
|
|
(let ((cur-item (the-as anim-test-seq-item (-> item-list head))))
|
|
(while (let ((item-node cur-item)) "is this node the end of the list. #t = end" (not (not (-> item-node next))))
|
|
(let ((item-node cur-item))
|
|
"return the next node in the list"
|
|
(let ((next-item (the-as anim-test-seq-item (-> item-node next))))
|
|
(if (and (not (logtest? (-> cur-item flags) (anim-test-item-flags end))) (not (anim-test-obj-item-valid? obj cur-item)))
|
|
(glst-remove (-> cur-seq item-list) cur-item))
|
|
(set! cur-item next-item)))))))
|
|
(let ((item-list (-> cur-seq item-list)))
|
|
"is the list empty, #t = empty"
|
|
(if (or (= (-> item-list tailpred) item-list)
|
|
(and (= (glst-num-elements (-> cur-seq item-list)) 1)
|
|
(begin
|
|
(let ((yes? #t)
|
|
(list (-> cur-seq item-list)))
|
|
"return the start of the list"
|
|
(let ((end-flag (the-as int (logand (-> (the-as anim-test-seq-item (-> list head)) flags) (anim-test-item-flags end)))))
|
|
(cmove-#f-zero only-end-item? end-flag yes?)))
|
|
only-end-item?)))
|
|
(glst-remove (-> obj seq-list) cur-seq)))
|
|
(set! cur-seq (the-as anim-test-sequence next-seq)))))))
|
|
(let ((seq-list (-> obj seq-list)))
|
|
"return the start of the list"
|
|
(let ((cur-seq (the-as anim-test-sequence (-> seq-list head))))
|
|
(while (let ((sequence-node cur-seq)) "is this node the end of the list. #t = end" (not (not (-> sequence-node next))))
|
|
(let ((sequence-node cur-seq))
|
|
"return the next node in the list"
|
|
(let ((next-node (-> sequence-node next)))
|
|
(logclear! (-> cur-seq flags) (anim-test-seq-flags anim-present))
|
|
(set! cur-seq (the-as anim-test-sequence next-node)))))))
|
|
#f)
|
|
|
|
(defbehavior anim-tester-real-post anim-tester ()
|
|
"Post the skeleton, but only while anim-playing is set: the state code clears
|
|
that flag whenever it could not set an animation up, and the joint state is
|
|
not safe to post then. Also moves the process by its own transv when
|
|
at-apply-align is on, and dumps the joint channels to the console when
|
|
at-show-joint-info is."
|
|
(when (logtest? (-> self flags) (anim-tester-flags anim-playing))
|
|
(if (logtest? (-> self flags) (anim-tester-flags at-apply-align))
|
|
(vector-v+! (-> self root trans) (-> self root trans) (-> self root transv)))
|
|
(ja-post)
|
|
(when (logtest? (-> self flags) (anim-tester-flags at-show-joint-info))
|
|
(draw-joint-spheres self)
|
|
(debug-print-channels (-> self skel) (the-as symbol *stdcon*))))
|
|
(none))
|
|
|
|
(defbehavior anim-tester-post anim-tester ()
|
|
"Post hook for anim-tester-process."
|
|
(anim-tester-real-post)
|
|
(none))
|
|
|
|
(defbehavior anim-tester-update-anim-info anim-tester ((item anim-test-seq-item))
|
|
"Recompute this frame's playback rate and frame limits from item. anim-gspeed
|
|
is the tester's own speed as a fraction, anim-speed folds in the row's
|
|
percentage on top of it. A negative rate plays the row backwards, which is
|
|
expressed by swapping first and last and then making both rates positive, so
|
|
the seek! in the state code always counts towards anim-last."
|
|
(set! (-> self anim-first) (-> item first-frame))
|
|
(set! (-> self anim-last) (-> item last-frame))
|
|
(set! (-> self anim-gspeed) (* 0.01 (the float (-> self speed))))
|
|
(set! (-> self anim-speed) (* (/ (-> self anim-gspeed) 100) (the float (-> item speed))))
|
|
(when (< (-> self anim-speed) 0.0)
|
|
(set! (-> self anim-first) (-> item last-frame))
|
|
(set! (-> self anim-last) (-> item first-frame)))
|
|
(set! (-> self anim-gspeed) (fabs (-> self anim-gspeed)))
|
|
(set! (-> self anim-speed) (fabs (-> self anim-speed))))
|
|
|
|
(defbehavior anim-tester-reset anim-tester ()
|
|
"Rebuild the draw and joint state for the current object.
|
|
|
|
The object is the one named by current-obj, or, if that name is not on the
|
|
list, whatever sits at list-con's current-index. Its skeleton and mesh become
|
|
a single LOD selected out to any distance, with a 10-metre bounding sphere so
|
|
it never culls, and a fresh joint animation channel set is posted once.
|
|
Complains to the console and leaves the old draw state alone if the object is
|
|
missing either its joint-geo or its mesh-geo."
|
|
(let ((obj-list (-> self obj-list)))
|
|
"is the list empty, #t = empty"
|
|
(cond
|
|
((= (-> obj-list tailpred) obj-list) (set! (-> self list-con current-index) 0) (set! (-> self current-obj) ""))
|
|
(else
|
|
(let ((obj (the-as anim-test-obj (glst-find-node-by-name (-> self obj-list) (-> self current-obj)))))
|
|
;; og:preserve-this added this if, sometimes the value is -1 which just crashes the game after here. nice work!
|
|
(if (< (-> self list-con current-index) 0) (set! (-> self list-con current-index) 0))
|
|
(when (not obj)
|
|
(if (>= (-> self list-con current-index) (glst-num-elements (-> self obj-list)))
|
|
(set! (-> self list-con current-index) (+ (glst-num-elements (-> self obj-list)) -1)))
|
|
(set! obj (the-as anim-test-obj (glst-get-node-by-index (-> self obj-list) (-> self list-con current-index))))
|
|
(set! (-> self current-obj) (-> obj privname)))
|
|
(cond
|
|
((or (not (-> obj joint-geo)) (not (-> obj mesh-geo)))
|
|
(format #t "what's this? ~A~%" (-> obj privname))
|
|
(format #t "it's missing a joint-geo, or a mesh-geo or a mesh-anim~%"))
|
|
(else
|
|
(let ((jgeo (-> obj joint-geo)))
|
|
(let ((mgeo (-> obj mesh-geo)))
|
|
(set! (-> self draw art-group) (-> obj obj-art-group))
|
|
(set! (-> self draw cur-lod) -1)
|
|
(set! (-> self draw jgeo) jgeo)
|
|
(set! (-> self draw sink-group) (-> *level* level-default pris-tex-foreground-sink-group))
|
|
(set! (-> self draw lod-set lod 0 geo) mgeo))
|
|
(set! (-> self draw lod-set lod 0 dist) 4095996000.0)
|
|
(set! (-> self draw bounds w) 40960.0)
|
|
(set! (-> self draw data-format) (the-as uint 1))
|
|
(let ((white-quad (-> (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0) quad)))
|
|
(set! (-> self draw color-mult quad) white-quad))
|
|
(let ((zero-quad (-> (new 'static 'vector) quad))) (set! (-> self draw color-emissive quad) zero-quad))
|
|
(set! (-> self draw secondary-interp) 0.0)
|
|
(set! (-> self draw shadow) #f)
|
|
(set! (-> self draw shadow-ctrl) #f)
|
|
(set! (-> self draw ripple) #f)
|
|
(set! (-> self draw level-index) (the-as uint 2))
|
|
(set! (-> self node-list) (make-nodes-from-jg jgeo *default-skel-template* 'debug)))
|
|
(set! (-> self skel effect) (new 'process 'effect-control self))
|
|
(fill-skeleton-cache self)
|
|
(lod-set! (-> self draw) 0)
|
|
(ja-channel-set! 0)
|
|
(ja-post)))))))
|
|
(none))
|
|
|
|
(defun anim-tester-disp-frame-num ((prefix string) (frame float) (artist-base float) (ctx font-context))
|
|
"Draw one frame-number column: prefix, then either \"min\"/\"max\" for the two
|
|
sentinels or the frame number with artist-base added, so the number matches
|
|
what the animator saw. Disabled - it returns before drawing anything."
|
|
;; this function does not work
|
|
(return (the pointer #f))
|
|
(local-vars (fmt-func (function _varargs_ object)))
|
|
(with-dma-buffer-add-bucket ((dma-buff (-> *display* frames (-> *display* on-screen) frame debug-buf)) (bucket-id debug)) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (cond
|
|
((= frame -1.0)
|
|
(let ((draw-adv draw-string-adv)) (format (clear *temp-string*) "~Smin" prefix) (draw-adv *temp-string* dma-buff ctx)))
|
|
((= frame -2.0)
|
|
(let ((draw-adv draw-string-adv)) (format (clear *temp-string*) "~Smax" prefix) (draw-adv *temp-string* dma-buff ctx)))
|
|
(else
|
|
(let ((draw-adv draw-string-adv))
|
|
(format (clear *temp-string*) "~S~3,,0f" prefix (+ frame artist-base))
|
|
(draw-adv *temp-string* dma-buff ctx))))))
|
|
|
|
(defbehavior anim-tester-standard-event-handler anim-tester ((proc process) (argc int) (message symbol) (block event-message-block))
|
|
"Handle the debug menu's requests.
|
|
|
|
'reset re-reads the current object and restarts playback, 'change-anim
|
|
restarts playback with whatever is selected now, the four pick/edit messages
|
|
open one of the lists and take the pad away from the camera, and
|
|
'save-sequences writes every object marked edited."
|
|
(case message
|
|
(('reset)
|
|
(process-disconnect self)
|
|
(logclear! (-> self flags) (anim-tester-flags anim-playing))
|
|
(when (!= (-> *anim-tester* 0 edit-mode) (anim-tester-edit-mode pick-object))
|
|
(set! (-> *debug-menu-context* is-hidden) #f)
|
|
(set! (-> *anim-tester* 0 edit-mode) (anim-tester-edit-mode none))
|
|
(set! *camera-read-buttons* #t))
|
|
(anim-tester-reset)
|
|
(go anim-tester-process))
|
|
(('change-anim) (go anim-tester-process))
|
|
(('pick-object) (set! (-> self edit-mode) (anim-tester-edit-mode pick-object)) (set! *camera-read-buttons* #f) #f)
|
|
(('pick-joint-anim)
|
|
(set! (-> self edit-mode) (anim-tester-edit-mode pick-joint-anim))
|
|
(set! *camera-read-buttons* #f)
|
|
#f)
|
|
(('pick-sequence) (set! (-> self edit-mode) (anim-tester-edit-mode pick-sequence)) (set! *camera-read-buttons* #f) #f)
|
|
(('edit-sequence) (set! (-> self edit-mode) (anim-tester-edit-mode edit-sequence)) (set! *camera-read-buttons* #f) #f)
|
|
(('save-sequences) (anim-tester-save-all-objects self))))
|
|
|
|
(defun anim-test-obj-list-handler ((cmd list-control-cmd) (ctrl list-control))
|
|
"listfunc for the object list: one row per mesh added by anim-tester-add-object,
|
|
marked with a * while it has unsaved sequence edits. X selects the object and
|
|
re-enters the process on it, square closes the list. Always returns #f apart
|
|
from the visible? command, which accepts every node."
|
|
(let ((obj (the-as anim-test-obj (-> ctrl the-node)))
|
|
(op cmd))
|
|
(cond
|
|
;; (list-control-cmd draw-line)
|
|
((zero? op)
|
|
(with-dma-buffer-add-bucket ((dma-buff (-> *display* frames (-> *display* on-screen) frame debug-buf)) (bucket-id debug)) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (let ((draw-xy draw-string-xy))
|
|
(format (clear *temp-string*)
|
|
"~S~S~S"
|
|
(if (= (-> ctrl the-index) (-> ctrl highlight-index)) ">" " ")
|
|
(if (logtest? (-> obj flags) (anim-test-obj-flags edited)) "*" " ")
|
|
(-> obj privname))
|
|
(draw-xy *temp-string*
|
|
dma-buff
|
|
(-> ctrl xpos)
|
|
(-> ctrl ypos)
|
|
(if (= (-> ctrl the-index) (-> ctrl current-index)) (font-color menu-flag-on) (font-color menu))
|
|
(font-flags shadow kerning)))))
|
|
((= op (list-control-cmd visible?)) (return #t))
|
|
((= op (list-control-cmd input))
|
|
(cond
|
|
((cpad-pressed? 0 up) (if (> (-> ctrl highlight-index) 0) (+! (-> ctrl highlight-index) -1)))
|
|
;; this lets the highlight sit one past the end for a frame;
|
|
;; display-list-control pulls it back next time round
|
|
((cpad-pressed? 0 down)
|
|
(if (< (-> ctrl highlight-index) (glst-num-elements (-> ctrl list))) (+! (-> ctrl highlight-index) 1)))
|
|
((cpad-pressed? 0 x)
|
|
(let ((tester (the-as object (-> ctrl list-owner))))
|
|
(set! (-> ctrl current-index) (-> ctrl the-index))
|
|
(set! (-> (the-as anim-tester tester) current-obj) (-> obj privname)))
|
|
(send-event (ppointer->process *anim-tester*) 'reset #f))
|
|
((cpad-pressed? 0 square)
|
|
(logclear! (-> *cpad-list* cpads 0 button0-abs 0) (pad-buttons square))
|
|
(logclear! (-> *cpad-list* cpads 0 button0-rel 0) (pad-buttons square))
|
|
(set! (-> *debug-menu-context* is-hidden) #f)
|
|
(set! (-> *anim-tester* 0 edit-mode) (anim-tester-edit-mode none))
|
|
(set! *camera-read-buttons* #t)
|
|
(logior! (-> *anim-tester* 0 flags) (anim-tester-flags return-to-menu)))))
|
|
((= op (list-control-cmd measure))
|
|
(let ((width (+ (length (-> obj privname)) 2)))
|
|
(set! width
|
|
(cond
|
|
((< (-> *ANIM_TESTER-bank* OBJECT_LIST_MIN_WIDTH) width) (empty) width)
|
|
(else (-> *ANIM_TESTER-bank* OBJECT_LIST_MIN_WIDTH))))
|
|
(set! (-> ctrl return-int) width)))
|
|
((= op (list-control-cmd draw-title))
|
|
(with-dma-buffer-add-bucket ((dma-buff (-> *display* frames (-> *display* on-screen) frame debug-buf)) (bucket-id debug)) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (draw-string-xy "----pick-object---"
|
|
dma-buff
|
|
(-> ctrl xpos)
|
|
(-> ctrl ypos)
|
|
(font-color menu)
|
|
(font-flags shadow kerning))))))
|
|
#f)
|
|
|
|
(defun anim-test-anim-list-handler ((cmd list-control-cmd) (ctrl list-control))
|
|
"listfunc for the animation list: the entries of one object's sequence list that
|
|
are still plain animations rather than named sequences, so up and down have to
|
|
skip over the sequences. When the control's user-info is 1 this list is the
|
|
picker inside the sequence editor and only moves its highlight; otherwise X
|
|
selects the animation to play and clears the object's play-sequence flag."
|
|
(let* ((seq (the-as anim-test-sequence (-> ctrl the-node)))
|
|
(obj (-> seq parent)))
|
|
(cond
|
|
;; (list-control-cmd draw-line)
|
|
((zero? cmd)
|
|
(with-dma-buffer-add-bucket ((dma-buff (-> *display* frames (-> *display* on-screen) frame debug-buf)) (bucket-id debug)) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (let ((draw-xy draw-string-xy))
|
|
(format (clear *temp-string*) "~S~S" (if (= (-> ctrl the-index) (-> ctrl highlight-index)) "> " " ") (-> seq privname))
|
|
(draw-xy *temp-string*
|
|
dma-buff
|
|
(-> ctrl xpos)
|
|
(-> ctrl ypos)
|
|
(if (= (-> ctrl the-index) (-> ctrl current-index)) (font-color menu-flag-on) (font-color menu))
|
|
(font-flags shadow kerning)))))
|
|
((= cmd (list-control-cmd visible?)) (return (not (logtest? (-> seq flags) (anim-test-seq-flags sequence)))))
|
|
((= cmd (list-control-cmd input))
|
|
(cond
|
|
((cpad-pressed? 0 up)
|
|
(let ((list (-> ctrl list)))
|
|
"is the list empty, #t = empty"
|
|
(when (not (= (-> list tailpred) list))
|
|
(let ((cur-node (the-as anim-test-sequence (glst-get-node-by-index (-> ctrl list) (-> ctrl highlight-index)))))
|
|
(loop
|
|
"return the previous node in the list"
|
|
(set! cur-node (the-as anim-test-sequence (-> cur-node prev)))
|
|
(let ((node cur-node)) "is this node the start of the list. #t = start" (if (not (-> node prev)) (goto cfg-25)))
|
|
(when (not (logtest? (-> cur-node flags) (anim-test-seq-flags sequence)))
|
|
(set! (-> ctrl highlight-index) (glst-get-node-index (-> ctrl list) cur-node))
|
|
(goto cfg-25))))))
|
|
(label cfg-25))
|
|
((cpad-pressed? 0 down)
|
|
(let ((list (-> ctrl list)))
|
|
"is the list empty, #t = empty"
|
|
(when (not (= (-> list tailpred) list))
|
|
(let ((cur-node (the-as anim-test-sequence (glst-get-node-by-index (-> ctrl list) (-> ctrl highlight-index)))))
|
|
(loop
|
|
"return the next node in the list"
|
|
(set! cur-node (the-as anim-test-sequence (-> cur-node next)))
|
|
(let ((node cur-node)) "is this node the end of the list. #t = end" (if (not (-> node next)) (goto cfg-39)))
|
|
(when (not (logtest? (-> cur-node flags) (anim-test-seq-flags sequence)))
|
|
(set! (-> ctrl highlight-index) (glst-get-node-index (-> ctrl list) cur-node))
|
|
(goto cfg-39))))))
|
|
(label cfg-39))
|
|
;; a picker inside the sequence editor only moves its highlight
|
|
((= (-> ctrl user-info) 1))
|
|
(else
|
|
(cond
|
|
((cpad-pressed? 0 x)
|
|
(set! (-> ctrl current-index) (-> ctrl the-index))
|
|
(set! (-> obj anim-index) (-> ctrl current-index))
|
|
(set! (-> obj anim-hindex) (-> ctrl highlight-index))
|
|
(logclear! (-> obj flags) (anim-test-obj-flags play-sequence))
|
|
(send-event (ppointer->process *anim-tester*) 'change-anim #f))
|
|
((cpad-pressed? 0 square)
|
|
(logclear! (-> *cpad-list* cpads 0 button0-abs 0) (pad-buttons square))
|
|
(logclear! (-> *cpad-list* cpads 0 button0-rel 0) (pad-buttons square))
|
|
(set! (-> *debug-menu-context* is-hidden) #f)
|
|
(set! (-> *anim-tester* 0 edit-mode) (anim-tester-edit-mode none))
|
|
(set! *camera-read-buttons* #t)
|
|
(logior! (-> *anim-tester* 0 flags) (anim-tester-flags return-to-menu)))))))
|
|
((= cmd (list-control-cmd measure))
|
|
(let ((width (+ (length (-> seq privname)) 2)))
|
|
(set! width
|
|
(cond
|
|
((< (-> *ANIM_TESTER-bank* ANIM_LIST_MIN_WIDTH) width) (empty) width)
|
|
(else (-> *ANIM_TESTER-bank* ANIM_LIST_MIN_WIDTH))))
|
|
(set! (-> ctrl return-int) width)))
|
|
((= cmd (list-control-cmd draw-title))
|
|
(with-dma-buffer-add-bucket ((dma-buff (-> *display* frames (-> *display* on-screen) frame debug-buf)) (bucket-id debug)) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (draw-string-xy "----pick-joint-anim----"
|
|
dma-buff
|
|
(-> ctrl xpos)
|
|
(-> ctrl ypos)
|
|
(font-color menu)
|
|
(font-flags shadow kerning))))))
|
|
#f)
|
|
|
|
(defun anim-test-sequence-list-handler ((cmd list-control-cmd) (ctrl list-control))
|
|
"listfunc for the sequence list: the named sequences of one object, so up and
|
|
down skip the plain animations. X opens the sequence editor on the highlighted
|
|
one and switches the object over to playing sequences."
|
|
(let* ((seq (the-as anim-test-sequence (-> ctrl the-node)))
|
|
(obj (-> seq parent)))
|
|
(cond
|
|
;; (list-control-cmd draw-line)
|
|
((zero? cmd)
|
|
(with-dma-buffer-add-bucket ((dma-buff (-> *display* frames (-> *display* on-screen) frame debug-buf)) (bucket-id debug)) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (let ((draw-xy draw-string-xy))
|
|
(format (clear *temp-string*)
|
|
"~S~S~S"
|
|
(if (= (-> ctrl the-index) (-> ctrl highlight-index)) ">" " ")
|
|
(if (logtest? (-> seq flags) (anim-test-seq-flags from-file)) "*" " ")
|
|
(-> seq privname))
|
|
(draw-xy *temp-string*
|
|
dma-buff
|
|
(-> ctrl xpos)
|
|
(-> ctrl ypos)
|
|
(if (= (-> ctrl the-index) (-> ctrl current-index)) (font-color menu-flag-on) (font-color menu))
|
|
(font-flags shadow kerning)))))
|
|
((= cmd (list-control-cmd visible?)) (return (logtest? (-> seq flags) (anim-test-seq-flags sequence))))
|
|
((= cmd (list-control-cmd input))
|
|
(cond
|
|
((cpad-pressed? 0 up)
|
|
(let ((list (-> ctrl list)))
|
|
"is the list empty, #t = empty"
|
|
(when (not (= (-> list tailpred) list))
|
|
(let ((cur-node (the-as anim-test-sequence (glst-get-node-by-index (-> ctrl list) (-> ctrl highlight-index)))))
|
|
(loop
|
|
"return the previous node in the list"
|
|
(set! cur-node (the-as anim-test-sequence (-> cur-node prev)))
|
|
(let ((node cur-node)) "is this node the start of the list. #t = start" (if (not (-> node prev)) (goto cfg-28)))
|
|
(when (logtest? (-> cur-node flags) (anim-test-seq-flags sequence))
|
|
(set! (-> ctrl highlight-index) (glst-get-node-index (-> ctrl list) cur-node))
|
|
(goto cfg-28))))))
|
|
(label cfg-28))
|
|
((cpad-pressed? 0 down)
|
|
(let ((list (-> ctrl list)))
|
|
"is the list empty, #t = empty"
|
|
(when (not (= (-> list tailpred) list))
|
|
(let ((cur-node (the-as anim-test-sequence (glst-get-node-by-index (-> ctrl list) (-> ctrl highlight-index)))))
|
|
(loop
|
|
"return the next node in the list"
|
|
(set! cur-node (the-as anim-test-sequence (-> cur-node next)))
|
|
(let ((node cur-node)) "is this node the end of the list. #t = end" (if (not (-> node next)) (goto cfg-42)))
|
|
(when (logtest? (-> cur-node flags) (anim-test-seq-flags sequence))
|
|
(set! (-> ctrl highlight-index) (glst-get-node-index (-> ctrl list) cur-node))
|
|
(goto cfg-42))))))
|
|
(label cfg-42))
|
|
((cpad-pressed? 0 x)
|
|
(set! (-> ctrl current-index) (-> ctrl the-index))
|
|
(set! (-> obj seq-index) (-> ctrl current-index))
|
|
(set! (-> obj seq-hindex) (-> ctrl highlight-index))
|
|
(logior! (-> obj flags) (anim-test-obj-flags play-sequence))
|
|
(set! (-> *anim-tester* 0 edit-mode) (anim-tester-edit-mode edit-sequence))
|
|
(send-event (ppointer->process *anim-tester*) 'change-anim #f))
|
|
((cpad-pressed? 0 square)
|
|
(logclear! (-> *cpad-list* cpads 0 button0-abs 0) (pad-buttons square))
|
|
(logclear! (-> *cpad-list* cpads 0 button0-rel 0) (pad-buttons square))
|
|
(set! (-> *debug-menu-context* is-hidden) #f)
|
|
(set! (-> *anim-tester* 0 edit-mode) (anim-tester-edit-mode none))
|
|
(set! *camera-read-buttons* #t)
|
|
(logior! (-> *anim-tester* 0 flags) (anim-tester-flags return-to-menu)))))
|
|
((= cmd (list-control-cmd measure))
|
|
(let ((width (+ (length (-> seq privname)) 2)))
|
|
(set! width
|
|
(cond
|
|
((< (-> *ANIM_TESTER-bank* PICK_LIST_MIN_WIDTH) width) (empty) width)
|
|
(else (-> *ANIM_TESTER-bank* PICK_LIST_MIN_WIDTH))))
|
|
(set! (-> ctrl return-int) width)))
|
|
((= cmd (list-control-cmd draw-title))
|
|
(with-dma-buffer-add-bucket ((dma-buff (-> *display* frames (-> *display* on-screen) frame debug-buf)) (bucket-id debug)) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (draw-string-xy "----pick-sequence---"
|
|
dma-buff
|
|
(-> ctrl xpos)
|
|
(-> ctrl ypos)
|
|
(font-color menu)
|
|
(font-flags shadow kerning))))))
|
|
#f)
|
|
|
|
;; Where each editor column starts and how wide it is, in characters. The first
|
|
;; is the animation name, then speed, blend, first, last, the four flag letters
|
|
;; and the three letters of "MID": move, insert, delete.
|
|
(define anim-test-field-highlight-lw
|
|
(new 'static
|
|
'inline-array
|
|
list-field
|
|
12
|
|
(new 'static 'list-field :left 2 :width 20)
|
|
(new 'static 'list-field :left 30 :width 4)
|
|
(new 'static 'list-field :left 35 :width 4)
|
|
(new 'static 'list-field :left 40 :width 3)
|
|
(new 'static 'list-field :left 44 :width 3)
|
|
(new 'static 'list-field :left 48 :width 1)
|
|
(new 'static 'list-field :left 49 :width 1)
|
|
(new 'static 'list-field :left 50 :width 1)
|
|
(new 'static 'list-field :left 51 :width 1)
|
|
(new 'static 'list-field :left 53 :width 1)
|
|
(new 'static 'list-field :left 54 :width 1)
|
|
(new 'static 'list-field :left 55 :width 1)))
|
|
|
|
(defun anim-tester-adjust-frame ((frame float) (num-frames float))
|
|
"Step a frame limit by one animation frame while the d-pad is held, and return
|
|
it. -1.0 means \"min\" and -2.0 means \"max\": stepping up from min gives frame
|
|
0 and stepping down from max gives the last real frame, while stepping past
|
|
either end saturates back onto the sentinel."
|
|
(cond
|
|
((cpad-hold? 0 down)
|
|
(cond
|
|
((= frame -2.0) (set! frame (+ -1.0 num-frames)))
|
|
((!= frame -1.0) (set! frame (+ -1.0 frame)) (if (< frame 0.0) (set! frame (the-as float -1.0))))))
|
|
((cpad-hold? 0 up)
|
|
(cond
|
|
((= frame -1.0) (set! frame (the-as float 0.0)))
|
|
((!= frame -2.0) (set! frame (+ 1.0 frame)) (if (>= frame num-frames) (set! frame (the-as float -2.0)))))))
|
|
(the-as float frame))
|
|
|
|
(defun anim-tester-pick-item-setup ((item anim-test-seq-item) (seq anim-test-sequence))
|
|
"Open the animation picker to the right of the sequence editor, highlighting the
|
|
animation item currently plays. user-info 1 tells the shared animation handler
|
|
that this is a picker, and editing-field makes the editor hand it the pad."
|
|
(let ((obj (-> seq parent)))
|
|
(set! (-> *anim-tester* 0 pick-con listfunc) anim-test-anim-list-handler)
|
|
(set! (-> *anim-tester* 0 pick-con left)
|
|
(+ (-> *ANIM_TESTER-bank* EDIT_LIST_X) (* (-> *ANIM_TESTER-bank* EDIT_PICK_X) (-> *DISP_LIST-bank* CHAR_WIDTH))))
|
|
(set! (-> *anim-tester* 0 pick-con top) (-> *ANIM_TESTER-bank* EDIT_LIST_Y))
|
|
(set! (-> *anim-tester* 0 pick-con list) (-> obj seq-list))
|
|
(set! (-> *anim-tester* 0 pick-con list-owner) (the-as uint obj))
|
|
(set! (-> *anim-tester* 0 pick-con user-info) 1)
|
|
;; walks the sequence list without doing anything with it
|
|
(let ((list (-> obj seq-list)))
|
|
"return the start of the list"
|
|
(let ((cur-node (-> list head)))
|
|
(while (let ((node cur-node)) "is this node the end of the list. #t = end" (not (not (-> node next))))
|
|
"return the next node in the list"
|
|
(set! cur-node (-> cur-node next)))))
|
|
(let ((match (glst-find-node-by-name (-> obj seq-list) (-> item privname))))
|
|
(when match
|
|
(set! (-> *anim-tester* 0 pick-con highlight-index) (glst-get-node-index (-> obj seq-list) match))
|
|
(set! (-> *anim-tester* 0 pick-con current-index) (-> *anim-tester* 0 pick-con highlight-index)))))
|
|
(logior! (-> *anim-tester* 0 flags) (anim-tester-flags editing-field))
|
|
(none))
|
|
|
|
(defun anim-test-seq-mark-as-edited ((seq anim-test-sequence))
|
|
"Turn seq into a named sequence, if it was not one already, and mark its object
|
|
as needing a save."
|
|
(logior! (-> seq parent flags) (anim-test-obj-flags edited))
|
|
(logior! (-> seq flags) (anim-test-seq-flags sequence))
|
|
(none))
|
|
|
|
(defun anim-test-edit-seq-insert-item ((item anim-test-seq-item) (seq anim-test-sequence))
|
|
"Insert a copy of item immediately in front of it. Copying the **END** row
|
|
yields a \"--blank--\" placeholder instead, so inserting on the last row appends
|
|
an empty one to fill in."
|
|
(let ((new-item (new 'debug 'anim-test-seq-item 1 "")))
|
|
(anim-test-seq-item-copy! new-item item)
|
|
(when (logtest? (-> new-item flags) (anim-test-item-flags end))
|
|
(logclear! (-> new-item flags) (anim-test-item-flags end))
|
|
(logior! (-> new-item flags) (anim-test-item-flags blank))
|
|
(let ((node new-item)) (set! (-> node privname) "--blank--")))
|
|
(glst-insert-before (-> seq item-list) item new-item))
|
|
(anim-test-seq-mark-as-edited seq)
|
|
(send-event (ppointer->process *anim-tester*) 'change-anim)
|
|
(none))
|
|
|
|
(defun anim-test-edit-sequence-list-handler ((cmd list-control-cmd) (ctrl list-control))
|
|
"listfunc for the sequence editor: one row per item, with the columns of
|
|
anim-test-item-field and the header \"-spd-blnd-1st-lst-flgs-mov-\".
|
|
|
|
Left and right move item-field along the row, skipping the three unimplemented
|
|
flag columns, and the highlighted column is drawn with a blue box behind it.
|
|
X acts on that column: on the name it opens the animation picker, on a number
|
|
it sets editing-field so up and down adjust the value (accelerating while
|
|
held, via inc-delay), on the blend flag it toggles it, and on the last three
|
|
it moves, inserts or deletes the row. The **END** row only accepts an insert."
|
|
(let ((item (the-as anim-test-seq-item (-> ctrl the-node)))
|
|
(seq (the-as anim-test-sequence (-> ctrl list-owner)))
|
|
(picking? (and (logtest? (-> *anim-tester* 0 flags) (anim-tester-flags editing-field)) (zero? (-> *anim-tester* 0 item-field)))))
|
|
(cond
|
|
;; (list-control-cmd draw-line)
|
|
((zero? cmd)
|
|
(let ((font-ctx (new 'stack
|
|
'font-context
|
|
*font-default-matrix*
|
|
(-> ctrl xpos)
|
|
(-> ctrl ypos)
|
|
(the-as float 0.0)
|
|
(if (= (-> ctrl the-index) (-> ctrl current-index)) (font-color menu-flag-on) (font-color menu))
|
|
(font-flags shadow kerning))))
|
|
(when (not picking?)
|
|
(when (= (-> ctrl the-index) (-> ctrl highlight-index))
|
|
(let* ((field-left (-> anim-test-field-highlight-lw (-> *anim-tester* 0 item-field) left))
|
|
(field-width (-> anim-test-field-highlight-lw (-> *anim-tester* 0 item-field) width))
|
|
(dma-buff (-> *display* frames (-> *display* on-screen) frame debug-buf))
|
|
(packet-start (-> dma-buff base)))
|
|
(draw-sprite2d-xy dma-buff
|
|
(+ (* field-left (-> *DISP_LIST-bank* CHAR_WIDTH)) 2 (-> ctrl xpos))
|
|
(+ (-> ctrl ypos) -1)
|
|
(+ (* field-width (-> *DISP_LIST-bank* CHAR_WIDTH)) 4)
|
|
(+ (-> *DISP_LIST-bank* TV_SPACING) 1)
|
|
(new 'static 'rgba :r #xc0 :g #xc0 :a #xff))
|
|
(let ((packet-end (-> dma-buff base)))
|
|
(let ((packet (the-as dma-packet (-> dma-buff base))))
|
|
(set! (-> packet dma) (new 'static 'dma-tag :id (dma-tag-id next)))
|
|
(set! (-> packet vif0) (new 'static 'vif-tag))
|
|
(set! (-> packet vif1) (new 'static 'vif-tag))
|
|
(set! (-> dma-buff base) (&+ (the-as pointer packet) 16)))
|
|
(dma-bucket-insert-tag (-> *display* frames (-> *display* on-screen) frame bucket-group)
|
|
(bucket-id debug)
|
|
packet-start
|
|
(the-as (pointer dma-tag) packet-end))))))
|
|
(with-dma-buffer-add-bucket ((dma-buff (-> *display* frames (-> *display* on-screen) frame debug-buf)) (bucket-id debug)) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (let ((draw-xy draw-string-xy))
|
|
(let ((fmt format))
|
|
(fmt (clear *temp-string*)
|
|
"~S~S~-27S"
|
|
(if (= (-> ctrl the-index) (-> ctrl highlight-index)) ">" " ")
|
|
(if (= (-> ctrl the-index) (-> seq playing-item)) "*" " ")
|
|
(-> item privname)))
|
|
(draw-xy *temp-string*
|
|
dma-buff
|
|
(-> ctrl xpos)
|
|
(-> ctrl ypos)
|
|
(if (= (-> ctrl the-index) (-> ctrl current-index)) (font-color menu-flag-on) (font-color menu))
|
|
(font-flags shadow kerning))))
|
|
(when (not picking?)
|
|
(with-dma-buffer-add-bucket ((dma-buff (-> *display* frames (-> *display* on-screen) frame debug-buf)) (bucket-id debug)) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (when (not (logtest? (-> item flags) (anim-test-item-flags end)))
|
|
(set-origin! font-ctx
|
|
(+ (-> ctrl xpos) (* (-> *ANIM_TESTER-bank* EDIT_STATS_X) (-> *DISP_LIST-bank* CHAR_WIDTH)))
|
|
(-> ctrl ypos))
|
|
(cond
|
|
((and (< (-> item speed) 0) (< -100 (-> item speed)))
|
|
(let ((draw-adv draw-string-adv))
|
|
(let ((fmt format)
|
|
(str (clear *temp-string*))
|
|
(fmt-str "-0.~1d")
|
|
(speed (abs (-> item speed))))
|
|
(fmt str fmt-str (/ (mod speed 100) 10)))
|
|
(draw-adv *temp-string* dma-buff font-ctx)))
|
|
(else
|
|
(let ((draw-adv draw-string-adv))
|
|
(let ((fmt format)
|
|
(str (clear *temp-string*))
|
|
(fmt-str "~2d.~1d")
|
|
(whole-speed (/ (-> item speed) 100))
|
|
(speed (abs (-> item speed))))
|
|
(fmt str fmt-str whole-speed (/ (mod speed 100) 10)))
|
|
(draw-adv *temp-string* dma-buff font-ctx))))
|
|
(let ((draw-adv draw-string-adv))
|
|
(format (clear *temp-string*) " ~4d" (-> item blend))
|
|
(draw-adv *temp-string* dma-buff font-ctx))
|
|
(anim-tester-disp-frame-num " " (-> item first-frame) (-> item artist-base) font-ctx)
|
|
(anim-tester-disp-frame-num " " (-> item last-frame) (-> item artist-base) font-ctx)
|
|
(let ((draw-adv draw-string-adv))
|
|
(format (clear *temp-string*)
|
|
" ~S~S~S~S"
|
|
(if (logtest? (-> item flags) (anim-test-item-flags wait-for-blend)) "B" "-")
|
|
"-"
|
|
"-"
|
|
"-")
|
|
(draw-adv *temp-string* dma-buff font-ctx))) (let* ((field-left (-> anim-test-field-highlight-lw 9 left))
|
|
(ctx font-ctx)
|
|
(x (+ (-> ctrl xpos) (* field-left (-> *DISP_LIST-bank* CHAR_WIDTH))))
|
|
(y (-> ctrl ypos)))
|
|
(set! (-> ctx origin x) (the float x))
|
|
(set! (-> ctx origin y) (the float y))) (draw-string-adv "MID" dma-buff font-ctx)))))
|
|
((= cmd (list-control-cmd visible?)) (return #t))
|
|
((= cmd (list-control-cmd input))
|
|
(cond
|
|
((logtest? (-> *anim-tester* 0 flags) (anim-tester-flags editing-field))
|
|
(let ((field (-> *anim-tester* 0 item-field)))
|
|
(cond
|
|
;; the name column
|
|
((zero? field)
|
|
(cond
|
|
((cpad-pressed? 0 x)
|
|
(logclear! (-> *anim-tester* 0 flags) (anim-tester-flags editing-field))
|
|
(let ((picked-seq (the-as anim-test-sequence
|
|
(glst-get-node-by-index (-> *anim-tester* 0 pick-con list) (-> *anim-tester* 0 pick-con highlight-index)))))
|
|
(when (and picked-seq
|
|
(let ((list (-> picked-seq item-list))) "is the list empty, #t = empty" (not (= (-> list tailpred) list))))
|
|
(let ((list (-> picked-seq item-list)))
|
|
"return the start of the list"
|
|
(let ((first-item (-> list head))) (anim-test-seq-item-copy! item (the-as anim-test-seq-item first-item))))
|
|
(anim-test-seq-mark-as-edited seq))))
|
|
((cpad-pressed? 0 square) (logclear! (-> *anim-tester* 0 flags) (anim-tester-flags editing-field)))))
|
|
((= field (anim-test-item-field move))
|
|
(cond
|
|
((not (cpad-hold? 0 x)) (logclear! (-> *anim-tester* 0 flags) (anim-tester-flags editing-field)))
|
|
((cpad-pressed? 0 up)
|
|
(let ((node item))
|
|
"return the previous node in the list"
|
|
(let* ((prev-item (-> node prev))
|
|
(prev-node prev-item))
|
|
"is this node the start of the list. #t = start"
|
|
(when (not (not (-> prev-node prev)))
|
|
(glst-remove (-> seq item-list) item)
|
|
(glst-insert-before (-> seq item-list) prev-item item)
|
|
(+! (-> ctrl current-index) -1)
|
|
(+! (-> ctrl highlight-index) -1)
|
|
(anim-test-seq-mark-as-edited seq)
|
|
(send-event (ppointer->process *anim-tester*) 'change-anim)))))
|
|
((cpad-pressed? 0 down)
|
|
(let ((node item))
|
|
"return the next node in the list"
|
|
(let* ((next-item (the-as anim-test-seq-item (-> node next)))
|
|
(next-node next-item))
|
|
"is this node the end of the list. #t = end"
|
|
(when (and (not (not (-> next-node next))) (not (logtest? (-> next-item flags) (anim-test-item-flags end))))
|
|
(glst-remove (-> seq item-list) item)
|
|
(glst-insert-after (-> seq item-list) next-item item)
|
|
(+! (-> ctrl current-index) 1)
|
|
(+! (-> ctrl highlight-index) 1)
|
|
(anim-test-seq-mark-as-edited seq)
|
|
(send-event (ppointer->process *anim-tester*) 'change-anim)))))))
|
|
((or (= field (anim-test-item-field speed))
|
|
(= field (anim-test-item-field blend))
|
|
(= field (anim-test-item-field first-frame))
|
|
(= field (anim-test-item-field last-frame)))
|
|
(cond
|
|
((not (cpad-hold? 0 x)) (logclear! (-> *anim-tester* 0 flags) (anim-tester-flags editing-field)))
|
|
((begin
|
|
(set! (-> ctrl current-index) (-> ctrl the-index))
|
|
(<= (-> *anim-tester* 0 inc-timer) 0))
|
|
(if (> (-> *anim-tester* 0 inc-delay) 0) (+! (-> *anim-tester* 0 inc-delay) -1))
|
|
(set! (-> *anim-tester* 0 inc-timer) (-> *anim-tester* 0 inc-delay))
|
|
(case (-> *anim-tester* 0 item-field)
|
|
(((anim-test-item-field speed))
|
|
(cond
|
|
((cpad-hold? 0 down)
|
|
(+! (-> item speed) -10)
|
|
(anim-test-seq-mark-as-edited seq)
|
|
(if (< (-> item speed) -300) (set! (-> item speed) -300)))
|
|
((cpad-hold? 0 up)
|
|
(+! (-> item speed) 10)
|
|
(anim-test-seq-mark-as-edited seq)
|
|
(if (< 1000 (-> item speed)) (set! (-> item speed) 1000)))))
|
|
(((anim-test-item-field blend))
|
|
(cond
|
|
((cpad-hold? 0 down)
|
|
(+! (-> item blend) -1)
|
|
(anim-test-seq-mark-as-edited seq)
|
|
(when (< (-> item blend) 0)
|
|
(set! (-> item blend) 0)
|
|
0))
|
|
((cpad-hold? 0 up)
|
|
(+! (-> item blend) 1)
|
|
(anim-test-seq-mark-as-edited seq)
|
|
(if (< 9999 (-> item blend)) (set! (-> item blend) 9999)))))
|
|
(((anim-test-item-field first-frame))
|
|
(let ((old-first (-> item first-frame)))
|
|
(set! (-> item first-frame) (anim-tester-adjust-frame (-> item first-frame) (-> item num-frames)))
|
|
(if (!= old-first (-> item first-frame)) (anim-test-seq-mark-as-edited seq))))
|
|
(((anim-test-item-field last-frame))
|
|
(let ((old-last (-> item last-frame)))
|
|
(set! (-> item last-frame) (anim-tester-adjust-frame (-> item last-frame) (-> item num-frames)))
|
|
(if (!= old-last (-> item last-frame)) (anim-test-seq-mark-as-edited seq))))))
|
|
(else (+! (-> *anim-tester* 0 inc-timer) -1)))
|
|
(when (or (cpad-pressed? 0 down) (cpad-pressed? 0 up))
|
|
(set! (-> *anim-tester* 0 inc-delay) (-> *DISP_LIST-bank* INC_DELAY))
|
|
(set! (-> *anim-tester* 0 inc-timer) 0)
|
|
0)))))
|
|
(else
|
|
(cond
|
|
((cpad-pressed? 0 up) (if (> (-> ctrl highlight-index) 0) (+! (-> ctrl highlight-index) -1)))
|
|
((cpad-pressed? 0 down)
|
|
(if (< (-> ctrl highlight-index) (glst-num-elements (-> ctrl list))) (+! (-> ctrl highlight-index) 1)))
|
|
((cpad-pressed? 0 left)
|
|
(+! (-> *anim-tester* 0 item-field) -1)
|
|
(if (< (-> *anim-tester* 0 item-field) 0) (set! (-> *anim-tester* 0 item-field) (the-as int (anim-test-item-field delete))))
|
|
(if (= (-> *anim-tester* 0 item-field) (anim-test-item-field unused-flag-3))
|
|
(set! (-> *anim-tester* 0 item-field) (the-as int (anim-test-item-field blend-flag)))))
|
|
((cpad-pressed? 0 right)
|
|
(+! (-> *anim-tester* 0 item-field) 1)
|
|
(when (>= (-> *anim-tester* 0 item-field) 12)
|
|
(set! (-> *anim-tester* 0 item-field) (the-as int (anim-test-item-field name)))
|
|
0)
|
|
(if (= (-> *anim-tester* 0 item-field) (anim-test-item-field unused-flag-1))
|
|
(set! (-> *anim-tester* 0 item-field) (the-as int (anim-test-item-field move)))))
|
|
((cpad-pressed? 0 square)
|
|
(logclear! (-> *cpad-list* cpads 0 button0-abs 0) (pad-buttons square))
|
|
(logclear! (-> *cpad-list* cpads 0 button0-rel 0) (pad-buttons square))
|
|
(set! (-> *anim-tester* 0 edit-mode) (anim-tester-edit-mode pick-sequence))
|
|
(logior! (-> *anim-tester* 0 flags) (anim-tester-flags return-to-menu)))
|
|
((cpad-pressed? 0 x)
|
|
(cond
|
|
((logtest? (-> item flags) (anim-test-item-flags end))
|
|
(let ((field (-> *anim-tester* 0 item-field)))
|
|
(if (= field (anim-test-item-field insert)) (anim-test-edit-seq-insert-item item seq))))
|
|
(else
|
|
(let ((field (-> *anim-tester* 0 item-field)))
|
|
(cond
|
|
;; the name column: open the animation picker
|
|
((zero? field) (anim-tester-pick-item-setup item seq))
|
|
((= field (anim-test-item-field insert)) (anim-test-edit-seq-insert-item item seq))
|
|
((= field (anim-test-item-field delete))
|
|
(when (not (logtest? (-> item flags) (anim-test-item-flags end)))
|
|
(anim-test-seq-mark-as-edited seq)
|
|
(glst-remove (-> seq item-list) item))
|
|
(send-event (ppointer->process *anim-tester*) 'change-anim))
|
|
(else
|
|
(when (not (logtest? (-> item flags) (anim-test-item-flags blank)))
|
|
(case (-> *anim-tester* 0 item-field)
|
|
(((anim-test-item-field blend-flag))
|
|
(anim-test-seq-mark-as-edited seq)
|
|
(let ((new-flags (logxor (-> item flags) (anim-test-item-flags wait-for-blend))))
|
|
(set! (-> item flags) new-flags)
|
|
new-flags))
|
|
(((anim-test-item-field unused-flag-1)) (the-as int #f))
|
|
(((anim-test-item-field unused-flag-2)) (the-as int #f))
|
|
(((anim-test-item-field unused-flag-3)) (the-as int #f))
|
|
(else
|
|
(logior! (-> *anim-tester* 0 flags) (anim-tester-flags editing-field))
|
|
(set! (-> *anim-tester* 0 inc-delay) (-> *DISP_LIST-bank* INC_DELAY))
|
|
(set! (-> *anim-tester* 0 inc-timer) 0)
|
|
0)))))))))))))
|
|
((= cmd (list-control-cmd measure)) (set! (-> ctrl return-int) (-> *ANIM_TESTER-bank* EDIT_LIST_MIN_WIDTH)))
|
|
((= cmd (list-control-cmd draw-title))
|
|
(with-dma-buffer-add-bucket ((dma-buff (-> *display* frames (-> *display* on-screen) frame debug-buf)) (bucket-id debug)) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (let ((draw-xy draw-string-xy))
|
|
(format (clear *temp-string*) "--Seq--(~-17S)--" (-> seq privname))
|
|
(draw-xy *temp-string* dma-buff (-> ctrl xpos) (-> ctrl ypos) (font-color menu) (font-flags shadow kerning))))
|
|
(cond
|
|
(picking? (display-list-control (-> *anim-tester* 0 pick-con)))
|
|
(else
|
|
(with-dma-buffer-add-bucket ((dma-buff (-> *display* frames (-> *display* on-screen) frame debug-buf)) (bucket-id debug)) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (draw-string-xy "-spd-blnd-1st-lst-flgs-mov-"
|
|
dma-buff
|
|
(+ (-> ctrl xpos) (* (-> *ANIM_TESTER-bank* EDIT_STATS_X) (-> *DISP_LIST-bank* CHAR_WIDTH)))
|
|
(-> ctrl ypos)
|
|
(font-color menu)
|
|
(font-flags shadow kerning))))))))
|
|
#f)
|
|
|
|
(defbehavior anim-tester-interface anim-tester ()
|
|
"Draw whichever list edit-mode selects, then remember it in old-mode. Prints an
|
|
error where the list would go if the object or sequence it needs is gone."
|
|
(let ((mode (-> self edit-mode)))
|
|
(cond
|
|
;; (anim-tester-edit-mode none)
|
|
((zero? mode)
|
|
(when (logtest? (-> *anim-tester* 0 flags) (anim-tester-flags return-to-menu))
|
|
(logclear! (-> *anim-tester* 0 flags) (anim-tester-flags return-to-menu))
|
|
(set-master-mode 'menu)))
|
|
((= mode (anim-tester-edit-mode pick-object)) (display-list-control (-> self list-con)))
|
|
((= mode (anim-tester-edit-mode pick-joint-anim))
|
|
(let ((obj (the-as anim-test-obj (glst-find-node-by-name (-> self obj-list) (-> self current-obj)))))
|
|
(cond
|
|
(obj
|
|
(set! (-> obj list-con listfunc) anim-test-anim-list-handler)
|
|
(set! (-> obj list-con current-index) (-> obj anim-index))
|
|
(set! (-> obj list-con highlight-index) (-> obj anim-hindex))
|
|
(display-list-control (-> obj list-con))
|
|
(set! (-> obj anim-index) (-> obj list-con current-index))
|
|
(set! (-> obj anim-hindex) (-> obj list-con highlight-index)))
|
|
(else
|
|
(with-dma-buffer-add-bucket ((dma-buff (-> *display* frames (-> *display* on-screen) frame debug-buf)) (bucket-id debug)) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (draw-string-xy "ERROR: current object not found"
|
|
dma-buff
|
|
(-> *ANIM_TESTER-bank* ANIM_LIST_X)
|
|
(-> *ANIM_TESTER-bank* ANIM_LIST_Y)
|
|
(font-color menu-func-bad)
|
|
(font-flags shadow kerning)))))))
|
|
((= mode (anim-tester-edit-mode pick-sequence))
|
|
(let ((obj (the-as anim-test-obj (glst-find-node-by-name (-> self obj-list) (-> self current-obj)))))
|
|
(cond
|
|
(obj
|
|
(set! (-> obj list-con listfunc) anim-test-sequence-list-handler)
|
|
(set! (-> obj list-con current-index) (-> obj seq-index))
|
|
(set! (-> obj list-con highlight-index) (-> obj seq-hindex))
|
|
(display-list-control (-> obj list-con))
|
|
(set! (-> obj seq-index) (-> obj list-con current-index))
|
|
(set! (-> obj seq-hindex) (-> obj list-con highlight-index)))
|
|
(else
|
|
(with-dma-buffer-add-bucket ((dma-buff (-> *display* frames (-> *display* on-screen) frame debug-buf)) (bucket-id debug)) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (draw-string-xy "ERROR: current object not found"
|
|
dma-buff
|
|
(-> *ANIM_TESTER-bank* ANIM_LIST_X)
|
|
(-> *ANIM_TESTER-bank* ANIM_LIST_Y)
|
|
(font-color menu-func-bad)
|
|
(font-flags shadow kerning)))))))
|
|
((= mode (anim-tester-edit-mode edit-sequence))
|
|
(let ((obj (the-as anim-test-obj (glst-find-node-by-name (-> self obj-list) (-> self current-obj)))))
|
|
(cond
|
|
(obj
|
|
(let ((seq (the-as anim-test-sequence (glst-get-node-by-index (-> obj seq-list) (-> obj list-con current-index)))))
|
|
(cond
|
|
(seq (display-list-control (-> seq list-con)))
|
|
(else
|
|
(with-dma-buffer-add-bucket ((dma-buff (-> *display* frames (-> *display* on-screen) frame debug-buf)) (bucket-id debug)) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (draw-string-xy "ERROR: current sequence not found"
|
|
dma-buff
|
|
(-> *ANIM_TESTER-bank* EDIT_LIST_X)
|
|
(-> *ANIM_TESTER-bank* EDIT_LIST_Y)
|
|
(font-color menu-func-bad)
|
|
(font-flags shadow kerning)))))))
|
|
(else
|
|
(with-dma-buffer-add-bucket ((dma-buff (-> *display* frames (-> *display* on-screen) frame debug-buf)) (bucket-id debug)) :bucket-group (-> *display* frames (-> *display* on-screen) frame bucket-group) (draw-string-xy "ERROR: current object not found"
|
|
dma-buff
|
|
(-> *ANIM_TESTER-bank* EDIT_LIST_X)
|
|
(-> *ANIM_TESTER-bank* EDIT_LIST_Y)
|
|
(font-color menu-func-bad)
|
|
(font-flags shadow kerning)))))))))
|
|
(set! (-> self old-mode) (-> self edit-mode))
|
|
(none))
|
|
|
|
(defun anim-tester-get-playing-item ((seq anim-test-sequence))
|
|
"Return the row seq should play now, advancing playing-item past the **END** and
|
|
\"--blank--\" rows and wrapping at the end of the list. Gives up and returns the
|
|
unplayable row it started from if the whole list is unplayable."
|
|
(let ((item ((the-as (function glst-list int anim-test-seq-item) glst-get-node-by-index) (-> seq item-list) (-> seq playing-item))))
|
|
(let ((first-item item)
|
|
(index (-> seq playing-item)))
|
|
(when (logtest? (-> item flags) (anim-test-item-flags end blank))
|
|
(loop
|
|
(+! index 1)
|
|
(if (>= index (glst-num-elements (-> seq item-list))) (set! index 0))
|
|
(set! item ((the-as (function glst-list int anim-test-seq-item) glst-get-node-by-index) (-> seq item-list) index))
|
|
(when (or (= item first-item) (not (logtest? (-> item flags) (anim-test-item-flags end blank))))
|
|
(set! (-> seq playing-item) index)
|
|
(return item)))))
|
|
item))
|
|
|
|
;; Play the selected animation or sequence, over and over. Each pass round the
|
|
;; loop re-reads the selection, because the lists can change it at any time, and
|
|
;; complains to *stdcon* and waits a frame if there is nothing to play.
|
|
(defstate anim-tester-process (anim-tester)
|
|
:event anim-tester-standard-event-handler
|
|
:enter
|
|
(behavior ()
|
|
(logior! (-> self flags) (anim-tester-flags just-entered)))
|
|
:trans
|
|
(behavior ()
|
|
(if (and (not (logtest? (-> self flags) (anim-tester-flags just-entered))) (= *master-mode* 'menu)) (anim-tester-interface))
|
|
(logclear! (-> self flags) (anim-tester-flags just-entered))
|
|
(when (!= *master-mode* 'menu)
|
|
(debug-print-channels (-> self skel) (the-as symbol *stdcon*))
|
|
(add-debug-x #t (bucket-id debug-no-zbuf) (-> self root trans) (new 'static 'rgba :r #xff :g #xff :b #xff :a #x80))))
|
|
:code
|
|
(behavior ()
|
|
;; obj, seq and item are the object, sequence and row being played; the cond
|
|
;; below picks them up one level at a time, so a failure at any level falls
|
|
;; through to the message for that level.
|
|
(local-vars (obj anim-test-obj) (item anim-test-seq-item) (seq anim-test-sequence))
|
|
(loop
|
|
(logclear! (-> self flags) (anim-tester-flags anim-playing))
|
|
(let ((obj-list (-> self obj-list)))
|
|
"is the list empty, #t = empty"
|
|
(cond
|
|
((= (-> obj-list tailpred) obj-list)
|
|
(format *stdcon* "anim-tester:no objects loaded~%")
|
|
(set! (-> self list-con current-index) 0)
|
|
(set! (-> self current-obj) "")
|
|
(suspend)
|
|
0)
|
|
((begin
|
|
(set! obj (the-as anim-test-obj (glst-find-node-by-name (-> self obj-list) (-> self current-obj))))
|
|
(when (not obj)
|
|
(if (>= (-> self list-con current-index) (glst-num-elements (-> self obj-list)))
|
|
(set! (-> self list-con current-index) (+ (glst-num-elements (-> self obj-list)) -1)))
|
|
(set! obj (the-as anim-test-obj (glst-get-node-by-index (-> self obj-list) (-> self list-con current-index))))
|
|
(set! (-> self current-obj) (-> obj privname)))
|
|
(let ((seq-list (-> obj seq-list))) "is the list empty, #t = empty" (= (-> seq-list tailpred) seq-list)))
|
|
(format *stdcon* "anim-tester:no anims loaded~%")
|
|
(format *stdcon* "displaying without anim not yet implement~%")
|
|
(logclear! (-> self flags) (anim-tester-flags anim-playing))
|
|
(suspend)
|
|
0)
|
|
((begin
|
|
(if (>= (-> obj list-con current-index) (glst-num-elements (-> obj seq-list)))
|
|
(set! (-> obj list-con current-index) (+ (glst-num-elements (-> obj seq-list)) -1)))
|
|
(set! seq
|
|
(the-as anim-test-sequence
|
|
(glst-get-node-by-index (-> obj seq-list)
|
|
(if (logtest? (-> obj flags) (anim-test-obj-flags play-sequence)) (-> obj seq-index) (-> obj anim-index)))))
|
|
(let ((item-list (-> seq item-list))) "is the list empty, #t = empty" (= (-> item-list tailpred) item-list)))
|
|
(format *stdcon* "anim-tester:no items in sequence ~A~%" (-> seq privname))
|
|
(format *stdcon* "displaying without anim not yet implement~%")
|
|
(suspend)
|
|
0)
|
|
((begin
|
|
(when (>= (-> seq playing-item) (glst-num-elements (-> seq item-list)))
|
|
(set! (-> seq playing-item) 0)
|
|
0)
|
|
(set! item (anim-tester-get-playing-item seq))
|
|
item)
|
|
(let ((anim (the-as art-joint-anim (lookup-art (-> obj obj-art-group) (-> item privname) art-joint-anim))))
|
|
(anim-tester-update-anim-info item)
|
|
(cond
|
|
(anim
|
|
(logior! (-> self flags) (anim-tester-flags anim-playing))
|
|
;; a row with a blend length pushes a channel so the previous
|
|
;; animation blends out; the length scales with the global speed
|
|
(if (nonzero? (-> item blend))
|
|
(ja-channel-push! 1 (the-as time-frame (the int (* (the float (-> item blend)) (-> self anim-gspeed)))))
|
|
(ja-channel-set! 1))
|
|
(cond
|
|
((= (-> self anim-first) -1.0) (ja :group! anim :num! min))
|
|
((= (-> self anim-first) -2.0) (ja :group! anim :num! max))
|
|
(else (ja :group! anim :num! (identity (-> self anim-first)))))
|
|
;; wait-for-blend holds the sequence here until the pushed
|
|
;; channel has finished blending in
|
|
(when (nonzero? (-> item blend))
|
|
(while (and (!= (-> self skel root-channel 0) (-> self skel channel))
|
|
(logtest? (-> item flags) (anim-test-item-flags wait-for-blend)))
|
|
(when (logtest? (-> self flags) (anim-tester-flags at-apply-align))
|
|
(compute-alignment! (-> self align))
|
|
(align! (-> self align)
|
|
(align-opts adjust-x-vel adjust-y-vel adjust-xz-vel keep-other-velocities adjust-quat)
|
|
(the-as float 1.0)
|
|
(the-as float 1.0)
|
|
(the-as float 1.0)))
|
|
(suspend)))
|
|
(until (ja-done? 0)
|
|
(when (logtest? (-> self flags) (anim-tester-flags at-apply-align))
|
|
(compute-alignment! (-> self align))
|
|
(align! (-> self align)
|
|
(align-opts adjust-x-vel adjust-y-vel adjust-xz-vel keep-other-velocities adjust-quat)
|
|
(the-as float 1.0)
|
|
(the-as float 1.0)
|
|
(the-as float 1.0)))
|
|
(suspend)
|
|
(anim-tester-update-anim-info item)
|
|
;; seek towards anim-last, which is either the end of the
|
|
;; animation, frame 0, or a real frame number
|
|
(let ((to-max? (= (-> self anim-last) -2.0)))
|
|
(cond
|
|
((or to-max? (>= (-> self anim-last) (-> self anim-first)))
|
|
(if (= (-> self anim-last) -2.0)
|
|
(ja :num! (seek! max (-> self anim-speed)))
|
|
(ja :num! (seek! (-> self anim-last) (-> self anim-speed)))))
|
|
((= (-> self anim-last) -1.0) (ja :num! (seek! 0.0 (-> self anim-speed))))
|
|
(else (ja :num! (seek! (-> self anim-last) (-> self anim-speed)))))))
|
|
(+! (-> seq playing-item) 1))
|
|
(else (format *stdcon* "anim ~A not found~%" (-> item privname)) (suspend) 0))))
|
|
(else (format *stdcon* "no anims~%") (suspend) 0)))))
|
|
:post anim-tester-post)
|
|
|
|
(defbehavior initialize-anim-tester anim-tester ()
|
|
"Set up the tester process: an empty object list, the object list control, a
|
|
24-channel joint control and an align control, and a position 10 metres in
|
|
front of the camera. Clears the menu process mask so the tester keeps running
|
|
while the debug menu is up."
|
|
(glst-init-list! (-> self obj-list))
|
|
(logclear! (-> self mask) (process-mask menu))
|
|
(set! (-> self speed) 100)
|
|
(set! (-> self current-obj) "")
|
|
(set! (-> self root) (new 'process 'trsqv))
|
|
(set! (-> self draw) (new 'process 'draw-control self (the-as art-joint-geo #f)))
|
|
(set! (-> self draw dma-add-func) dma-add-process-drawable)
|
|
(set! (-> self skel) (new 'process 'joint-control 24))
|
|
(set! (-> self align) (new 'process 'align-control self))
|
|
(set! (-> self list-con listfunc) anim-test-obj-list-handler)
|
|
(set! (-> self list-con left) (-> *ANIM_TESTER-bank* OBJECT_LIST_X))
|
|
(set! (-> self list-con top) (-> *ANIM_TESTER-bank* OBJECT_LIST_Y))
|
|
(set! (-> self list-con list) (-> self obj-list))
|
|
(set! (-> self list-con list-owner) (the-as uint self))
|
|
(quaternion-identity! (-> self root quat))
|
|
(vector-identity! (-> self root scale))
|
|
(position-in-front-of-camera! (-> self root trans) (the-as float 40960.0) (the-as float 4096.0))
|
|
(set! (-> self event-hook) anim-tester-standard-event-handler)
|
|
(anim-tester-reset)
|
|
(go anim-tester-process)
|
|
(none))
|
|
|
|
(defun anim-tester-string-get-frame!! ((out list-field) (str string))
|
|
"Parse the next argument of str as a frame number into (-> out left), accepting
|
|
\"min\" and \"max\" in either case as the -1 and -2 sentinels. Returns #f, leaving
|
|
out alone, when str holds no argument."
|
|
(cond
|
|
((string-get-arg!! *temp-string* str)
|
|
(cond
|
|
((or (string= *temp-string* "max") (string= *temp-string* "MAX")) (set! (-> out left) -2))
|
|
((or (string= *temp-string* "min") (string= *temp-string* "MIN")) (set! (-> out left) -1))
|
|
(else (set! (-> out left) (string->int *temp-string*))))
|
|
#t)
|
|
(else #f)))
|
|
|
|
(defun anim-tester-load-object-seqs ((tester anim-tester) (name string))
|
|
"Read data/<name>.obinf back into tester. Not implemented: it returns #f, so
|
|
from-file is never set and edited sequences do not survive a restart."
|
|
#f)
|
|
|
|
(defun anim-tester-save-object-seqs ((obj anim-test-obj))
|
|
"Write obj's sequences to data/<object>.obinf as text: one Anim or Sequence
|
|
block per entry of the sequence list, one Item line per row inside it, and the
|
|
whole thing wrapped in Object/EndObject. The **END** and \"--blank--\" rows are
|
|
skipped, and from-file is cleared on everything written."
|
|
(let ((fmt format)
|
|
(dest 0)
|
|
(fmt-str "saving object ~s to ~s~%")
|
|
(name (-> obj privname)))
|
|
(format (clear *temp-string*) "data/~s.obinf" (-> obj privname))
|
|
(fmt dest fmt-str name *temp-string*))
|
|
;; og:preserve-this hack
|
|
(let ((file (new 'stack 'file-stream (string-format "data/~s.obinf" (-> obj privname)) 'write)))
|
|
(format file "major-version 0~%")
|
|
(format file "minor-version 0~%")
|
|
(format file "Object \"~S\" ~d~%" (-> obj privname) 0)
|
|
(let ((seq-list (-> obj seq-list)))
|
|
"return the start of the list"
|
|
(let ((cur-seq (the-as anim-test-sequence (-> seq-list head))))
|
|
(while (let ((sequence-node cur-seq)) "is this node the end of the list. #t = end" (not (not (-> sequence-node next))))
|
|
(logclear! (-> cur-seq flags) (anim-test-seq-flags from-file))
|
|
(format file
|
|
" ~S \"~S\" ~d~%"
|
|
(if (logtest? (-> cur-seq flags) (anim-test-seq-flags sequence)) "Sequence" "Anim")
|
|
(-> cur-seq privname)
|
|
0)
|
|
(let ((item-list (-> cur-seq item-list)))
|
|
"return the start of the list"
|
|
(let ((cur-item (the-as anim-test-seq-item (-> item-list head))))
|
|
(while (let ((item-node cur-item)) "is this node the end of the list. #t = end" (not (not (-> item-node next))))
|
|
(when (not (logtest? (-> cur-item flags) (anim-test-item-flags end blank)))
|
|
(format file " Item \"~S\" ~d ~d " (-> cur-item privname) (-> cur-item speed) (-> cur-item blend))
|
|
(anim-tester-num-print file (-> cur-item first-frame))
|
|
(format file " ")
|
|
(anim-tester-num-print file (-> cur-item last-frame))
|
|
(format file " ~S~%" (if (logtest? (-> cur-item flags) (anim-test-item-flags wait-for-blend)) "B" "-")))
|
|
"return the next node in the list"
|
|
(set! cur-item (the-as anim-test-seq-item (-> cur-item next))))))
|
|
(format file " ~S~%" (if (logtest? (-> cur-seq flags) (anim-test-seq-flags sequence)) "EndSequence" "EndAnim"))
|
|
"return the next node in the list"
|
|
(set! cur-seq (the-as anim-test-sequence (-> cur-seq next))))))
|
|
(format file "EndObject~%")
|
|
(file-stream-close file)))
|
|
|
|
(defun anim-tester-save-all-objects ((tester anim-tester))
|
|
"Write out every object marked edited and clear the flag. Always returns #f."
|
|
(let ((obj-list (-> tester obj-list)))
|
|
"return the start of the list"
|
|
(let ((cur-obj (the-as anim-test-obj (-> obj-list head))))
|
|
(while (let ((node cur-obj)) "is this node the end of the list. #t = end" (not (not (-> node next))))
|
|
(when (logtest? (-> cur-obj flags) (anim-test-obj-flags edited))
|
|
(logclear! (-> cur-obj flags) (anim-test-obj-flags edited))
|
|
(anim-tester-save-object-seqs cur-obj))
|
|
"return the next node in the list"
|
|
(set! cur-obj (the-as anim-test-obj (-> cur-obj next))))))
|
|
#f)
|
|
|
|
(defun anim-tester-add-newobj ((tester anim-tester) (name string) (ag art-group))
|
|
"Add the contents of art group ag to the object list.
|
|
|
|
The group is scanned in order. Its first merc-ctrl becomes the object, named
|
|
after that element; its first art-joint-geo becomes the object's skeleton; and
|
|
every art-joint-anim becomes one of the object's sequences, holding a single
|
|
row whose length and artist-base come from the animation itself. One group
|
|
therefore yields one object, however many meshes it holds.
|
|
|
|
A sequence already on the list is kept and only marked anim-present, so
|
|
reloading a group preserves hand-built playlists; whatever is left unmarked
|
|
afterwards is dropped by anim-test-obj-remove-invalid. Selects the object and
|
|
restarts the tester on it. name is unused - the names come out of ag."
|
|
(let ((obj (the-as anim-test-obj #f))
|
|
(first-obj (the-as anim-test-obj #f)))
|
|
(let ((jgeo-elt (the-as art-element #f))
|
|
(found? (the-as structure #f)))
|
|
(dotimes (idx (-> ag length))
|
|
(cond
|
|
((and (= (-> ag data idx type) merc-ctrl) (not obj))
|
|
(let ((mesh-elt (-> ag data idx)))
|
|
(set! found? (and obj found?))
|
|
(if found? (anim-test-obj-remove-invalid obj))
|
|
(anim-tester-load-object-seqs tester (-> mesh-elt name))
|
|
(set! obj (the-as anim-test-obj (glst-find-node-by-name (-> tester obj-list) (-> mesh-elt name))))
|
|
(set! found? (if obj #t #f))
|
|
(cond
|
|
((the-as symbol found?))
|
|
(else (set! obj (new 'global 'anim-test-obj 1 (-> mesh-elt name) ag)) (glst-add-tail (-> tester obj-list) obj)))
|
|
(anim-test-obj-init obj (the-as list-control tester))
|
|
(set! (-> obj obj-art-group) ag)
|
|
(set! (-> obj mesh-geo) (the-as merc-ctrl mesh-elt)))
|
|
(set! (-> obj joint-geo) (the-as art-joint-geo jgeo-elt))
|
|
(if (not first-obj) (set! first-obj obj)))
|
|
((= (-> ag data idx type) art-joint-geo)
|
|
(if (and obj (not (-> obj joint-geo))) (set! (-> obj joint-geo) (the-as art-joint-geo (-> ag data idx))))
|
|
(if (not jgeo-elt) (set! jgeo-elt (-> ag data idx))))
|
|
((= (-> ag data idx type) art-joint-anim)
|
|
(when obj
|
|
(let* ((anim-elt (-> ag data idx))
|
|
(seq (the-as anim-test-sequence (glst-find-node-by-name (-> obj seq-list) (-> anim-elt name)))))
|
|
(when (not seq)
|
|
(set! seq (new 'debug 'anim-test-sequence 1 (-> anim-elt name)))
|
|
(glst-add-tail (-> obj seq-list) seq)
|
|
(anim-test-sequence-init seq obj)
|
|
(let ((item (new 'debug 'anim-test-seq-item 1 (-> anim-elt name)))) (glst-add-tail (-> seq item-list) item)))
|
|
(set! (-> seq parent) obj)
|
|
(set! (-> seq flags) (logior (-> seq flags) (anim-test-seq-flags anim-present)))
|
|
(let ((item-list (-> seq item-list)))
|
|
"is the list empty, #t = empty"
|
|
(when (not (= (-> item-list tailpred) item-list))
|
|
(let ((list (-> seq item-list)))
|
|
"return the start of the list"
|
|
(let ((first-item (the-as anim-test-seq-item (-> list head))))
|
|
(set! (-> first-item num-frames) (the float (-> (the-as art-joint-anim anim-elt) data 0 length)))
|
|
(set! (-> first-item artist-base) (-> (the-as art-joint-anim anim-elt) artist-base))
|
|
(set! (-> first-item parent) seq)))
|
|
seq)))))
|
|
(else))))
|
|
(if obj (anim-test-obj-remove-invalid obj))
|
|
(when first-obj
|
|
(set! (-> tester current-obj) (-> first-obj privname))
|
|
(set! (-> tester list-con current-index) (glst-get-node-index (-> tester obj-list) first-obj))))
|
|
(send-event tester 'reset #f))
|
|
|
|
(defun anim-tester-stop ()
|
|
"Kill the tester process, if it is running."
|
|
(when *anim-tester*
|
|
(kill-by-name 'anim-tester *active-pool*)
|
|
(set! *anim-tester* (the-as (pointer anim-tester) #f))
|
|
#f))
|
|
|
|
(defun anim-tester-start ()
|
|
"Restart the tester with an empty object list and point the orbit camera at it."
|
|
(anim-tester-stop)
|
|
(set! *anim-tester* (process-spawn anim-tester :init initialize-anim-tester :from *16k-dead-pool*))
|
|
(set! *camera-orbit-target* *anim-tester*)
|
|
(send-event *camera* 'change-state cam-orbit 0)
|
|
#f)
|
|
|
|
(defun anim-tester-add-object ((name string))
|
|
"Load art group name into the global heap and add it to the tester, starting the
|
|
tester first if it is not running. Prints an error if the group is not found."
|
|
(let ((ag (load-to-heap-by-name (-> *level* level-default art-group) name #t global 0)))
|
|
(cond
|
|
(ag
|
|
(if (not *anim-tester*) (anim-tester-start))
|
|
(if *anim-tester* (anim-tester-add-newobj (the-as anim-tester (ppointer->process *anim-tester*)) name ag)))
|
|
(else (format 0 "ERROR:no object (~A)n" name))))
|
|
(none))
|
|
|
|
(defun anim-tester-set-name ((name string))
|
|
"Rename the selected sequence of the selected object to name. Refuses names that
|
|
another sequence of the same object already uses, and only works on a named
|
|
sequence, not on a plain animation."
|
|
(cond
|
|
((zero? (length name)) (format #t "ERROR: no name~%"))
|
|
((and *anim-tester*
|
|
(let ((obj-list (-> *anim-tester* 0 obj-list)))
|
|
"is the list empty, #t = empty"
|
|
(not (= (-> obj-list tailpred) obj-list))))
|
|
(let ((obj (the-as anim-test-obj (glst-find-node-by-name (-> *anim-tester* 0 obj-list) (-> *anim-tester* 0 current-obj)))))
|
|
(cond
|
|
(obj
|
|
(let ((seq (the-as anim-test-sequence (glst-get-node-by-index (-> obj seq-list) (-> obj list-con current-index)))))
|
|
(cond
|
|
(seq
|
|
(cond
|
|
((logtest? (-> seq flags) (anim-test-seq-flags sequence))
|
|
(let ((old-name (the-as object (-> seq privname))))
|
|
(let ((node seq)) (set! (-> node privname) ""))
|
|
(cond
|
|
((glst-find-node-by-name (-> obj seq-list) name)
|
|
(format #t "ERROR: another sequence is already using that name (~S)~%" name)
|
|
(set! (-> seq privname) (the-as string old-name)))
|
|
(else (set! old-name name) (set! (-> seq privname) (the-as string old-name))))
|
|
old-name))
|
|
(else (format #t "ERROR: no sequence selected~%"))))
|
|
(else (format #t "ERROR: no sequence selected~%")))))
|
|
(else (format #t "ERROR:no object selected~%")))))
|
|
(else (format #t "ERROR:no object loaded~%"))))
|
|
|
|
(defun anim-tester-add-sequence ((name string))
|
|
"Create an empty named sequence on the selected object and open the editor on
|
|
it. If the name is already taken the existing sequence is opened instead. A new
|
|
sequence starts with just its **END** row."
|
|
(cond
|
|
((zero? (length name)) (format #t "ERROR: no name~%"))
|
|
((and *anim-tester*
|
|
(let ((obj-list (-> *anim-tester* 0 obj-list)))
|
|
"is the list empty, #t = empty"
|
|
(not (= (-> obj-list tailpred) obj-list))))
|
|
(let ((obj (the-as anim-test-obj (glst-find-node-by-name (-> *anim-tester* 0 obj-list) (-> *anim-tester* 0 current-obj)))))
|
|
(cond
|
|
(obj
|
|
(let ((existing (glst-find-node-by-name (-> obj seq-list) name)))
|
|
(cond
|
|
(existing
|
|
(format #t "ERROR: there is already a sequence with the name ~S~%" name)
|
|
(set! (-> obj list-con current-index) (glst-get-node-index (-> obj seq-list) existing))
|
|
(send-event (ppointer->process *anim-tester*) 'edit-sequence))
|
|
(else
|
|
(let ((seq (new 'global 'anim-test-sequence 1 name)))
|
|
(glst-add-tail (-> obj seq-list) seq)
|
|
(set! (-> seq list-con listfunc) anim-test-edit-sequence-list-handler)
|
|
(set! (-> seq list-con left) (-> *ANIM_TESTER-bank* EDIT_LIST_X))
|
|
(set! (-> seq list-con top) (-> *ANIM_TESTER-bank* EDIT_LIST_Y))
|
|
(set! (-> seq list-con list) (-> seq item-list))
|
|
(set! (-> seq list-con list-owner) (the-as uint seq))
|
|
(set! (-> seq parent) obj)
|
|
(anim-test-seq-mark-as-edited seq)
|
|
(logior! (-> seq flags) (anim-test-seq-flags sequence))
|
|
(set! (-> obj list-con current-index) (glst-get-node-index (-> obj seq-list) seq))
|
|
(set! (-> obj seq-index) (-> obj list-con current-index))
|
|
(set! (-> obj seq-hindex) (-> obj list-con current-index))
|
|
(let ((end-item (new 'debug 'anim-test-seq-item 1 "**END**")))
|
|
(logior! (-> end-item flags) (anim-test-item-flags end))
|
|
(glst-add-tail (-> seq item-list) end-item)
|
|
(set! (-> end-item parent) seq)))
|
|
(send-event (ppointer->process *anim-tester*) 'edit-sequence)))))
|
|
(else (format #t "ERROR:no object selected~%")))))
|
|
(else (format #t "ERROR:no object loaded~%")))
|
|
(none))
|