Skip to content

Commit

Permalink
custom levels: etie and build actor support for jak2/3 (#3851)
Browse files Browse the repository at this point in the history
Custom levels for Jak 2/3 now support envmapped TIE geometry. The TIE
extract was also changed to ignore materials that have the specular flag
set, but are missing a roughness texture.

Jak 2/3 now also support the `build-actor` tool.

The `build-custom-level` and `build-actor` macros now have a few new
options:

- Both now have a `force-run` option (`#f` by default) that, when set to
`#t`, will always run level/art group generation even if the output
files are up to date.
- `build-custom-level` has a `gen-fr3` option (`#t` by default) that,
when set to `#f`, will skip generating the FR3 file for the custom level
and only generate the GOAL level file to skip the potentially slow
process of finding and adding art groups and textures. Useful for when
you want to temporarily edit only the GOAL side of the level (such as
entity placement, etc.).
- `build-actor` has a `texture-bucket` option (default 0) which will
determine what DMA sink group the model will be placed in, which is
useful to determine the draw order of the model. Previously, this was
omitted, resulting in shadows not drawing over custom actors because the
actors were put in a bucket that is drawn after shadows (this behavior
can be restored with `:texture-bucket #f`).
  • Loading branch information
Hat-Kid authored Feb 1, 2025
1 parent 23b86d9 commit 710f3ac
Show file tree
Hide file tree
Showing 46 changed files with 3,023 additions and 532 deletions.
5 changes: 3 additions & 2 deletions common/util/gltf_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -685,14 +685,15 @@ bool material_has_envmap(const tinygltf::Material& mat) {
}

bool envmap_is_valid(const tinygltf::Material& mat) {
if (material_has_envmap(mat) && mat.pbrMetallicRoughness.metallicRoughnessTexture.index < 0) {
auto envmap = material_has_envmap(mat);
if (envmap && mat.pbrMetallicRoughness.metallicRoughnessTexture.index < 0) {
lg::warn(fmt::format(
"Material \"{}\" has specular property set, but is missing a metallic roughness texture, "
"ignoring envmap!",
mat.name));
return false;
}
return true;
return envmap;
}

std::optional<int> find_single_skin(const tinygltf::Model& model,
Expand Down
15 changes: 15 additions & 0 deletions custom_assets/jak2/levels/test-zone/test-zone.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@
// Removed so that the release builds don't have to double-decompile the game
// "art_groups": ["prsn-torture-ag"],

// If you have any custom models in the "custom_assets/jak2/models/custom_levels" folder that you want to use in your level, add them to this list.
// Note: Like with art groups, these should also be added to your level's .gd file.
"custom_models": ["test-actor"],

// Any textures you want to include in your custom level.
// This is mainly useful for textures which are not in the common level files and have no art group associated with them.
// To get a list of all the textures, you can extract all of the game's textures
Expand Down Expand Up @@ -99,6 +103,17 @@
"lump": {
"name": "test-torture"
}
},

{
"trans": [5.41, 3.5, 28.42], // translation
"etype": "test-actor", // actor type
"game_task": 0, // associated game task (for powercells, etc)
"quat": [0, 0, 0, 1], // quaternion
"bsphere": [5.41, 3.5, 28.42, 10], // bounding sphere
"lump": {
"name": "test-actor"
}
}
]
}
2 changes: 2 additions & 0 deletions custom_assets/jak2/levels/test-zone/testzone.gd
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@
("TSZ.DGO"
(
"prison-obs.o"
"test-zone-obs.o"
"test-actor-ag.go"
"test-zone.go"
))
Binary file not shown.
15 changes: 15 additions & 0 deletions custom_assets/jak3/levels/test-zone/test-zone.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@
// All art groups you want to use in your custom level. Will add their models and corresponding textures to the FR3 file.
// "art_groups": [],

// If you have any custom models in the "custom_assets/jak3/models/custom_levels" folder that you want to use in your level, add them to this list.
// Note: Like with art groups, these should also be added to your level's .gd file.
"custom_models": ["test-actor"],

// Any textures you want to include in your custom level.
// This is mainly useful for textures which are not in the common level files and have no art group associated with them.
// To get a list of all the textures, you can extract all of the game's textures
Expand Down Expand Up @@ -87,6 +91,17 @@
"lump": {
"name": "test-eco"
}
},

{
"trans": [5.41, 3.5, 28.42], // translation
"etype": "test-actor", // actor type
"game_task": 0, // associated game task (for powercells, etc)
"quat": [0, 0, 0, 1], // quaternion
"bsphere": [5.41, 3.5, 28.42, 10], // bounding sphere
"lump": {
"name": "test-actor"
}
}
]
}
8 changes: 5 additions & 3 deletions custom_assets/jak3/levels/test-zone/testzone.gd
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

;; the actual file name still needs to be 8.3
("TSZ.DGO"
("test-zone.go"
)
)
(
"test-zone-obs.o"
"test-actor-ag.go"
"test-zone.go"
))
Binary file not shown.
8 changes: 4 additions & 4 deletions goal_src/jak1/game.gp
Original file line number Diff line number Diff line change
Expand Up @@ -218,15 +218,15 @@
)
)

(defmacro build-custom-level (name)
(defmacro build-custom-level (name &key (force-run #f) &key (gen-fr3 #t))
(let* ((path (string-append "custom_assets/jak1/levels/" name "/" name ".jsonc")))
`(defstep :in ,path
`(defstep :in '(,path ,(symbol->string force-run) ,(symbol->string gen-fr3))
:tool 'build-level
:out '(,(string-append "$OUT/obj/" name ".go")))))

(defmacro build-actor (name &key (gen-mesh #f))
(defmacro build-actor (name &key (gen-mesh #f) &key (force-run #f) &key (texture-bucket 0))
(let* ((path (string-append "custom_assets/jak1/models/custom_levels/" name ".glb")))
`(defstep :in '(,path ,(symbol->string gen-mesh))
`(defstep :in '(,path ,(symbol->string gen-mesh) ,(symbol->string force-run) ,(if (integer? texture-bucket) (int->string texture-bucket) (symbol->string texture-bucket)))
:tool 'build-actor
:out '(,(string-append "$OUT/obj/" name "-ag.go")))))

Expand Down
25 changes: 25 additions & 0 deletions goal_src/jak2/engine/data/art-h.gc
Original file line number Diff line number Diff line change
Expand Up @@ -420,5 +420,30 @@
)
)

(defmacro def-actor (name &key (idle #f) &key (lods #f) &key (art (idle-ja)) &key (joints ()) &key (shadow 0) &key bounds &key (longest-edge 0.0) &key (texture-level 0) &key (sort 0) &key (version 7) &key (origin-joint-index 0) &key (shadow-joint-index 0) &key (light-index 0))
`(begin
(def-art-elt ,(string->symbol-format "{}-ag" name) ,(string->symbol-format "{}-lod0-jg" name) 0)
(def-art-elt ,(string->symbol-format "{}-ag" name) ,(string->symbol-format "{}-lod0-mg" name) 1)
,@(apply-i (lambda (x i)
`(def-art-elt ,(string->symbol-format "{}-ag" name) ,(string->symbol-format "{}-{}" name x) ,(+ i 2)))
art)
,@(apply-i (lambda (x i) `(def-joint-node ,(string->symbol-format "{}-lod0-jg" name) ,(symbol->string x) ,(1+ i))) joints)
(defskelgroup ,(string->symbol-format "*{}-sg*" name)
,name
,(string->symbol-format "{}-lod0-jg" name)
,(if idle (string->symbol-format "{}-{}" name idle) (string->symbol-format "{}-{}" name (car art)))
,(if lods
`(,@(apply (lambda (x) `(,(string->symbol-format "{}-{}-mg" name (car x)) (meters ,(cadr x)))) lods))
`((,(string->symbol-format "{}-lod0-mg" name) (meters 999999))))
:version ,version
:shadow ,shadow
:bounds (static-spherem ,@bounds)
:longest-edge ,longest-edge
:texture-level ,texture-level
:sort ,sort
:origin-joint-index ,origin-joint-index
:shadow-joint-index ,shadow-joint-index
:light-index ,light-index)))

(import "goal_src/jak2/engine/data/art-elts.gc")
(import "goal_src/jak2/engine/data/joint-nodes.gc")
6 changes: 6 additions & 0 deletions goal_src/jak2/game.gp
Original file line number Diff line number Diff line change
Expand Up @@ -300,8 +300,14 @@
;; it should point to the .jsonc file that specifies the level.
(build-custom-level "test-zone")
;; the DGO file
(goal-src "levels/test-zone/test-zone-obs.gc" "process-focusable")
(custom-level-cgo "TSZ.DGO" "test-zone/testzone.gd")

;; generate the art group for a custom actor.
;; requires a .glb model file in custom_assets/jak1/models/custom_levels
;; to also generate a collide-mesh, add :gen-mesh #t
(build-actor "test-actor" :force-run #t :gen-mesh #t)

;;;;;;;;;;;;;;;;;;;;;
;; ANIMATIONS
;;;;;;;;;;;;;;;;;;;;;
Expand Down
101 changes: 101 additions & 0 deletions goal_src/jak2/levels/test-zone/test-zone-obs.gc
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
;;-*-Lisp-*-
(in-package goal)
(deftype test-actor (process-drawable)
((root collide-shape-moving :override)
(birth-time time-frame)
(base vector :inline)
(old-base vector :inline)
(bob-offset int64)
(bob-amount float))
(:methods
(init-collision! (_type_) object))
(:state-methods
idle))

(def-actor test-actor
:bounds (0 0 0 5))

(defmethod init-collision! ((this test-actor))
(let ((cshape (new 'process 'collide-shape-moving this (collide-list-enum hit-by-player))))
(set! (-> cshape dynam) (copy *standard-dynamics* 'process))
(set! (-> cshape reaction) cshape-reaction-default)
(set! (-> cshape no-reaction) (the (function collide-shape-moving collide-query vector vector object) nothing))
(let ((mesh (new 'process 'collide-shape-prim-mesh cshape (the uint 0) (the uint 0))))
(set! (-> mesh prim-core collide-as) (collide-spec pusher))
(set! (-> mesh collide-with) (collide-spec jak player-list))
(set! (-> mesh prim-core action) (collide-action solid rideable))
(set! (-> mesh transform-index) 0)
(set! (-> cshape total-prims) (the uint 1))
(set! (-> cshape root-prim) mesh)
(set-vector! (-> mesh local-sphere) 0.0 (meters 0) 0.0 (meters 5)))
(pusher-init cshape)
(set! (-> cshape nav-radius) (* 0.75 (-> cshape root-prim local-sphere w)))
(let ((prim (-> cshape root-prim)))
(set! (-> cshape backup-collide-as) (-> prim prim-core collide-as))
(set! (-> cshape backup-collide-with) (-> prim prim-core collide-with))
)
(set! (-> this root) cshape)))

(defmethod init-from-entity! ((this test-actor) (e entity-actor))
(logior! (-> this mask) (process-mask enemy))
(init-collision! this)
(process-drawable-from-entity! this e)
(set! (-> this bob-amount) 1024.0)
(set! (-> this bob-offset)
(+ (the int (-> this root trans x)) (the int (-> this root trans y)) (the int (-> this root trans z))))
(set-time! (-> this birth-time))
(vector-copy! (-> this base) (-> this root trans))
(vector-copy! (-> this old-base) (-> this root trans))
(initialize-skeleton this *test-actor-sg* '())
(logclear! (-> this mask) (process-mask actor-pause))
(transform-post)
(go-virtual idle :proc this)
(none))

(defbehavior test-actor-init-by-other test-actor ((pos vector))
(logior! (-> self mask) (process-mask enemy))
(init-collision! self)
(initialize-skeleton self *test-actor-sg* '())
(vector-copy! (-> self root trans) pos)
(quaternion-identity! (-> self root quat))
(vector-identity! (-> self root scale))
(set! (-> self bob-amount) 1024.0)
(set! (-> self bob-offset)
(+ (the int (-> self root trans x)) (the int (-> self root trans y)) (the int (-> self root trans z))))
(set-time! (-> self birth-time))
(vector-copy! (-> self base) (-> self root trans))
(vector-copy! (-> self old-base) (-> self root trans))
(logclear! (-> self mask) (process-mask actor-pause))
(transform-post)
(go-virtual idle))

(defstate idle (test-actor)
:virtual #t
:event
(behavior ((proc process) (argc int) (message symbol) (block event-message-block))
(case message
(('attack 'touch)
; (if (= (-> proc type) target)
; (send-event proc 'attack #f (static-attack-info ((shove-up (meters 2.5)) (shove-back (meters 7.5)))))
; )
#t)))
:code
(behavior ()
(loop
(quaternion-rotate-y! (-> self root quat) (-> self root quat) (* (degrees 45) (seconds-per-frame)))
(let ((bob (-> self bob-amount)))
(when (< 0.0 bob)
(set! (-> self root trans y)
(+ (-> self base y)
(* bob
(sin (* 109.22667 (the float (mod (+ (- (current-time) (-> self birth-time)) (-> self bob-offset)) (seconds 2))))))))
(update-transforms (-> self root))))
; (debug-draw-tris (-> (res-lump-struct (-> self draw art-group data 0 extra) 'collide-mesh-group (array collide-mesh)) 0) self 2)
; (dotimes (i (-> self node-list length))
; (let* ((joint (-> self node-list data i)) (jpos (vector<-cspace! (new-stack-vector0) joint)))
; (add-debug-sphere #t (bucket-id debug) jpos (meters 0.1) (static-rgba 0 #xff 0 #x40))
; (add-debug-text-sphere (!= (-> joint joint) #f) (bucket-id debug) jpos (meters 0.1) (-> joint joint name) (static-rgba 0 #xff 0 #x40))
; )
; )
(suspend)))
:post transform-post)
10 changes: 8 additions & 2 deletions goal_src/jak2/lib/project-lib.gp
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,18 @@
)
)

(defmacro build-custom-level (name)
(defmacro build-custom-level (name &key (force-run #f) &key (gen-fr3 #t))
(let* ((path (string-append "custom_assets/jak2/levels/" name "/" name ".jsonc")))
`(defstep :in ,path
`(defstep :in '(,path ,(symbol->string force-run) ,(symbol->string gen-fr3))
:tool 'build-level2
:out '(,(string-append "$OUT/obj/" name ".go")))))

(defmacro build-actor (name &key (gen-mesh #f) &key (force-run #f) &key (texture-bucket 0))
(let* ((path (string-append "custom_assets/jak2/models/custom_levels/" name ".glb")))
`(defstep :in '(,path ,(symbol->string gen-mesh) ,(symbol->string force-run) ,(if (integer? texture-bucket) (int->string texture-bucket) (symbol->string texture-bucket)))
:tool 'build-actor2
:out '(,(string-append "$OUT/obj/" name "-ag.go")))))

(defmacro group (name &rest stuff)
`(defstep :in ""
:tool 'group
Expand Down
27 changes: 27 additions & 0 deletions goal_src/jak3/engine/data/art-h.gc
Original file line number Diff line number Diff line change
Expand Up @@ -486,5 +486,32 @@ Each process-drawable has a draw-control."
)
)

(defmacro def-actor (name &key (idle #f) &key (lods #f) &key (art (idle-ja)) &key (joints ()) &key (shadow 0) &key bounds &key (longest-edge 0.0) &key (texture-level 0) &key (sort 0) &key (version 7) &key (origin-joint-index 0) &key (shadow-joint-index 0) &key (light-index 0) &key (global-effects 0) &key (clothing ()))
`(begin
(def-art-elt ,(string->symbol-format "{}-ag" name) ,(string->symbol-format "{}-lod0-jg" name) 0)
(def-art-elt ,(string->symbol-format "{}-ag" name) ,(string->symbol-format "{}-lod0-mg" name) 1)
,@(apply-i (lambda (x i)
`(def-art-elt ,(string->symbol-format "{}-ag" name) ,(string->symbol-format "{}-{}" name x) ,(+ i 2)))
art)
,@(apply-i (lambda (x i) `(def-joint-node ,(string->symbol-format "{}-lod0-jg" name) ,(symbol->string x) ,(1+ i))) joints)
(defskelgroup ,(string->symbol-format "*{}-sg*" name)
,name
,(string->symbol-format "{}-lod0-jg" name)
,(if idle (string->symbol-format "{}-{}" name idle) (string->symbol-format "{}-{}" name (car art)))
,(if lods
`(,@(apply (lambda (x) `(,(string->symbol-format "{}-{}-mg" name (car x)) (meters ,(cadr x)))) lods))
`((,(string->symbol-format "{}-lod0-mg" name) (meters 999999))))
:version ,version
:shadow ,shadow
:bounds (static-spherem ,@bounds)
:longest-edge ,longest-edge
:texture-level ,texture-level
:sort ,sort
:origin-joint-index ,origin-joint-index
:shadow-joint-index ,shadow-joint-index
:light-index ,light-index
:global-effects ,global-effects
:clothing ,clothing)))

(import "goal_src/jak3/engine/data/art-elts.gc")
(import "goal_src/jak3/engine/data/joint-nodes.gc")
6 changes: 6 additions & 0 deletions goal_src/jak3/game.gp
Original file line number Diff line number Diff line change
Expand Up @@ -403,8 +403,14 @@
;; it should point to the .jsonc file that specifies the level.
(build-custom-level "test-zone")
;; the DGO file
(goal-src "levels/test-zone/test-zone-obs.gc" "process-focusable")
(custom-level-cgo "TSZ.DGO" "test-zone/testzone.gd")

;; generate the art group for a custom actor.
;; requires a .glb model file in custom_assets/jak3/models/custom_levels
;; to also generate a collide-mesh, add :gen-mesh #t
(build-actor "test-actor" :gen-mesh #t)

;;;;;;;;;;;;;;;;;;;;;
;; ANIMATIONS
;;;;;;;;;;;;;;;;;;;;;
Expand Down
Loading

0 comments on commit 710f3ac

Please sign in to comment.