refactor asset loading

This commit is contained in:
2023-08-29 16:15:37 -06:00
parent e2bb87f3d5
commit f3722910e4
6 changed files with 145 additions and 67 deletions

10
memory-fix.org Normal file
View File

@@ -0,0 +1,10 @@
* DONE for all asset types, make the Director define load*()
* DONE instead of requiring Cloneable<Set>, add Director.cloneSet()
* DONE in all newMethods take a path, not the object.
** DONE allow for alternative loading with the object directly, like how some props in HollywooFlixel are animated via ActorFlxSprite
** DONE if the assetpath doesn't start with assets/, call (assetPath "<typeDir>" "<path>")
** DONE make loading things at runtime a compiler error
* TODO in all new* methods, save the path that was loaded
* TODO in all with_ macros, mark those assets as dirty (needing to be reloaded)
* TODO Movie.scavenge before cleanup
* TODO in all new* methods, don't load the new thing if it exists already

View File

@@ -26,7 +26,7 @@ typedef AutoZConfig = {
};
@:keepSub
interface Director<Set:Cloneable<Set>, Actor, Sound, Song, Prop, VoiceTrack, Camera, LightSource:Jsonable<LightSource>> {
interface Director<Set, Actor, Sound, Song, Prop, VoiceTrack, Camera, LightSource:Jsonable<LightSource>> {
var movie(default, default):Movie<Set, Actor, Sound, Song, Prop, VoiceTrack, Camera, LightSource>;
function autoZConfig():Option<AutoZConfig>;
@@ -51,15 +51,19 @@ interface Director<Set:Cloneable<Set>, Actor, Sound, Song, Prop, VoiceTrack, Cam
function defineStagePosition(camera:Camera, submit:StagePosition->Void, ?oldPosition:StagePosition):Void;
function defineLightSource(submit:LightSource->Void):Void;
function loadSet(path:String):Set;
function cloneSet(set:Set):Set;
function showSet(set:Set, time:SceneTime, perspective:ScenePerspective, appearance:Appearance, camera:Camera, cc:Continuation):Void;
function hideSet(set:Set, camera: Camera, cc:Continuation):Void;
function showLighting(sceneTime:SceneTime, lightSources:Array<LightSource>, camera:Camera):Void;
function hideLighting():Void;
function loadActor(path:String):Actor;
function showCharacter(character:Character<Actor>, appearance:Appearance, camera:Camera, cc:Continuation):Void;
function hideCharacter(character:Character<Actor>, camera:Camera, cc:Continuation):Void;
function loadSound(path:String):Sound;
function playSound(sound:Sound, volumeMod:Float, ?cc:Continuation):Void;
function getSoundLength(sound:Sound):Float;
function stopSound(sound:Sound):Void;
@@ -67,11 +71,13 @@ interface Director<Set:Cloneable<Set>, Actor, Sound, Song, Prop, VoiceTrack, Cam
function showCaption(description:String, id:Int):Void;
function hideCaption(id:Int):Void;
function loadSong(path:String):Song;
function playSong(song:Song, volumeMod:Float, loop:Bool, waitForEnd:Bool, cc:Continuation):Void;
function getSongLength(song:Song):Float;
function changeSongVolume(volumeMod:Float, cc:Continuation):Void;
function stopSong():Void;
function loadVoiceTrack(path:String):VoiceTrack;
function playVoiceTrack(track:VoiceTrack, volumeMod:Float, start:Float, end:Float, cc:Continuation):Void;
function stopVoiceTrack(track:VoiceTrack):Void;
@@ -87,6 +93,7 @@ interface Director<Set:Cloneable<Set>, Actor, Sound, Song, Prop, VoiceTrack, Cam
function showBlackScreen():Void;
function hideBlackScreen():Void;
function loadProp(path:String):Prop;
function showProp(prop:Prop, position:StagePosition, appearance:Appearance, camera:Camera, cc:Continuation):Void;
function hideProp(prop:Prop, camera:Camera, cc:Continuation):Void;

View File

@@ -73,15 +73,29 @@
(director.showTitleCard ["LOADING"]
(makeCC
(set isLoading true)
(director.doLoading ,preloadFuncs
(makeCC
(set isLoading false)
(.start (director.shortcutHandler))
(director.hideTitleCard)
// When all loading is done, prompt to start obs recording automatically:
(#if debug
(promptToRecord cc)
(cc)))))))
(let [loadVoiceTrack _loadVoiceTrack
addVoiceTrack _addVoiceTrack
noVoiceTracks _noVoiceTracks
loadProp _loadProp
addProp _addProp
loadSong _loadSong
addSong _addSong
loadActor _loadActor
addActor _addActor
loadSet _loadSet
addSet _addSet
newSceneFromSet _newSceneFromSet
loadSound _loadSound
addSound _addSound]
(director.doLoading ,preloadFuncs
(makeCC
(set isLoading false)
(.start (director.shortcutHandler))
(director.hideTitleCard)
// When all loading is done, prompt to start obs recording automatically:
(#if debug
(promptToRecord cc)
(cc))))))))
@:keep
(method doCleanup []
(director.cleanup)

View File

@@ -20,10 +20,6 @@ import kiss_tools.TimerWithPause;
using kiss.FuzzyMapTools;
typedef Cloneable<T> = {
function clone():T;
}
enum DelayHandling {
Auto;
AutoWithSkip;
@@ -65,4 +61,4 @@ enum PlayMode {
* Model/controller of a Hollywoo film, and main execution script
*/
@:build(kiss.Kiss.build())
class Movie<Set:Cloneable<Set>, Actor, Sound, Song, Prop, VoiceTrack, Camera, LightSource:Jsonable<LightSource>> extends AsyncEmbeddedScript2 {}
class Movie<Set, Actor, Sound, Song, Prop, VoiceTrack, Camera, LightSource:Jsonable<LightSource>> extends AsyncEmbeddedScript2 {}

View File

@@ -142,11 +142,36 @@
(throw "No handler for custom dialog type $type")))
(otherwise (director.showDialog actorName dialogType wryly text skipCC)))))))
(method noVoiceTracks [actorName]
(dictSet voiceTracksPerActor actorName 0)
(dictSet voiceLines actorName (new FuzzyMap<VoiceLine>)))
(defMacro withIndexedPath [pathVar typeDefaultDir &body body]
(#if (or sys hxnodejs)
`(let [,pathVar
(if (StringTools.startsWith ,pathVar assetDir)
,pathVar
(assetPath ,typeDefaultDir ,pathVar))]
,@body)
body))
(method newVoiceTrack [actorName :VoiceTrack track :String lineJson]
// Methods for loading new assets in a hollywoo movie follow a special naming convention.
// _add*() is a method which takes the asset DIRECTLY and adds it to the corresponding asset map.
// This is to be used in cases where the asset needs to be loaded specially in a way
// the Director's load*() function can't handle.
// _load*() is a method which takes the asset's PATH and lets the Director load the asset object.
//
// Neither of these is not meant to be called directly, as a precaution against loading new assets
// at Movie runtime. However, within the context of (preload...) blocks, they will be
// bound to these names:
// add*()
// new*() At which point you can use them. Therefore, if you accidentally try to call new*()
// outside of a preload block, your code won't compile.
// Which is desired because (preload...) blocks are meant to be allowed at any point in a script,
// even following the usage of the assets they load. Runtime loading allows for errors
// caused by moving the load calls or asset usages around so the load doesn't precede the usage.
(method _loadVoiceTrack [actorName :String path :String lineJson]
(withIndexedPath path "vo"
(_addVoiceTrack actorName (director.loadVoiceTrack path) lineJson)))
(method _addVoiceTrack [actorName :VoiceTrack track :String lineJson]
(assert isLoading)
(let [actorNumVoiceTracks (or (dictGet voiceTracksPerActor actorName) 0)
trackKey "${actorName}${actorNumVoiceTracks}"
:haxe.DynamicAccess<Dynamic> lines (Json.parse lineJson)]
@@ -161,6 +186,68 @@
(dictSet voiceLines actorName (new FuzzyMap<VoiceLine>)))
(dictSet (dictGet voiceLines actorName) key (objectWith [start line.start end line.end] trackKey alts))))))
(method _noVoiceTracks [actorName]
(assert isLoading)
(dictSet voiceTracksPerActor actorName 0)
(dictSet voiceLines actorName (new FuzzyMap<VoiceLine>)))
(method _loadProp [name :String path]
(withIndexedPath path "images"
(_addProp name (director.loadProp path))))
(method _addProp [name :Prop prop]
(assert isLoading)
(dictSet props name prop))
(method _loadSong [name :String path]
(withIndexedPath path "music"
(_addSong name (director.loadSong path))))
(method _addSong [name :Song song]
(assert isLoading)
(dictSet songs name song))
(method _loadActor [name :String path]
(withIndexedPath path "images"
(_addActor name (director.loadActor path))))
(method _addActor [name :Actor actor]
(assert isLoading)
(dictSet actors name actor))
(method _loadSet [name :String path]
(withIndexedPath path "images"
(_addSet name (director.loadSet path))))
(method _addSet [name :Set set]
(assert isLoading)
(dictSet sets name set))
(method _newSceneFromSet [name :String setKey :SceneTime time :ScenePerspective perspective :Camera camera]
(assert isLoading)
(dictSet scenes name (objectWith
[
set
(director.cloneSet (dictGet sets setKey))
characters
(new FuzzyMap<Character<Actor>>)
props
(new FuzzyMap<StageProp<Prop>>)
camera
camera
]
time
perspective)))
(method _loadSound [name :String path :String description]
(withIndexedPath path "sounds"
(_addSound name (director.loadSound path) description)))
(method _addSound [name :Sound s :String description]
(assert isLoading)
(dictSet sounds name s)
(dictSet soundDescriptions name description))
(method _ccForEach <>[T] [:Iterable<T> collection :(T,Continuation)->Void do_ :Continuation finalCC]
(let [:Iterator<T> iter (collection.iterator)]
(withFunctions
@@ -263,8 +350,10 @@
(#when (or sys hxnodejs)
(prop :FuzzyMap<FuzzyMap<String>> assetPaths (new FuzzyMap))
(prop &mut :String assetDir "")
(prop :Array<Array<String>> loadedCredits [])
(method _indexAssetPaths [:String assetDir]
(set this.assetDir assetDir)
(let [dirParts (assetDir.split "/")]
(doFor part dirParts
(dictSet assetPaths part (new FuzzyMap))))
@@ -445,15 +534,8 @@
((dictGet runners label))
})))))
(super)))
// END Parent class definitions
(defMacro indexAssetPaths [dir]
`(preload
(_indexAssetPaths ,dir)))
(#unless subclass
(super))
@:keep
(method :Void _strobe [:Bool skipping :Bool prop :String actorOrPropKey :Float strobeSec :Int times &opt :Continuation cc]
(when skipping
@@ -497,6 +579,13 @@
(when cc
(TimerWithPause.delay cc (* strobeSec 2 (+ 1 times)))))))
// END Parent class definitions
(defMacro indexAssetPaths [dir]
`(preload
(_indexAssetPaths ,dir)))
// Some real magic happens here. This macro defines a method, AND a reader macro
// for calling it with skipping and cc passed automatically if cc is an argument.
// GOTCHA: DO NOT use (method) directly in this file!!
@@ -574,26 +663,6 @@
}))
(otherwise (throw "Unsupported delay type $delayHandling")))))
(hollywooMethod newSet [name :Set set]
(assert isLoading)
(dictSet sets name set))
(hollywooMethod newSceneFromSet [name :String setKey :SceneTime time :ScenePerspective perspective :Camera camera]
(assert isLoading)
(dictSet scenes name (objectWith
[
set
(.clone (dictGet sets setKey))
characters
(new FuzzyMap<Character<Actor>>)
props
(new FuzzyMap<StageProp<Prop>>)
camera
camera
]
time
perspective)))
(hollywooMethod setSceneSong [:String scene :String songKey &opt :Float volumeMod :Continuation cc]
(dictSet sceneMusic scene songKey)
(dictSet sceneMusicVolume scene volumeMod)
@@ -633,12 +702,6 @@
.camera (dictGet scenes name)
cc)))))
(hollywooMethod newSound [name :Sound s :String description]
(assert isLoading)
(dictSet sounds name s)
(dictSet soundDescriptions name description))
(hollywooMethod playSound [:Bool skipping name :Continuation cc &opt :Float volumeMod :Bool waitForEnd]
(when skipping
(cc)
@@ -692,10 +755,6 @@
(playAgain))))
(cc))
(hollywooMethod newSong [name :Song song]
(assert isLoading)
(dictSet songs name song))
// This is never skipped because the music might be expected to continue on to the place
// we skip to:
(hollywooMethod playSong [name :Continuation cc &opt :Float volumeMod :Bool loop :Bool waitForEnd]
@@ -719,10 +778,6 @@
(director.stopSong)
(cc))
(hollywooMethod newActor [name :Actor actor]
(assert isLoading)
(dictSet actors name actor))
(hollywooMethod autoZProcess [:StagePosition position :Continuation cc]
// handle auto z recursively
(ifLet [(Some (objectWith zPerLayer frontLayer)) (director.autoZConfig)]
@@ -802,10 +857,6 @@
// TODO moveCharacter remove them, add them to another scene
// TODO moveCharacterAndFollow remove them, add them to another scene, set that the scene
(hollywooMethod newProp [name :Prop prop]
(assert isLoading)
(dictSet props name prop))
(hollywooMethod addProp [name :Dynamic position :Continuation cc]
(let [name (FuzzyMapTools.bestMatch props name)
prop (dictGet props name)

View File

@@ -38,7 +38,7 @@ typedef StageProp<Prop> = {
prop:Prop
};
typedef Scene<Set:Cloneable<Set>, Actor, Prop, Camera> = {
typedef Scene<Set, Actor, Prop, Camera> = {
set:Set,
characters:FuzzyMap<Character<Actor>>,
props:FuzzyMap<StageProp<Prop>>,