package assets import ( "fmt" "sync" "gitea.boner.be/bdnugget/goonscape/logging" "gitea.boner.be/bdnugget/goonscape/types" rl "github.com/gen2brain/raylib-go/raylib" ) var ( assetMutex sync.RWMutex loadedModels map[string]types.ModelAsset audioMutex sync.Mutex audioInitialized bool ) func init() { loadedModels = make(map[string]types.ModelAsset) } // Helper function to load animations for a model func loadModelAnimations(animPaths map[string]string) (types.AnimationSet, error) { var animSet types.AnimationSet // Load idle animations if specified if idlePath, ok := animPaths["idle"]; ok { idleAnims := rl.LoadModelAnimations(idlePath) if len(idleAnims) > 0 { animSet.Idle = idleAnims rl.TraceLog(rl.LogInfo, "Loaded idle animation: %s (%d frames, %f seconds)", idlePath, idleAnims[0].FrameCount, float32(idleAnims[0].FrameCount)/60.0) } } // Load walk animations if specified if walkPath, ok := animPaths["walk"]; ok { walkAnims := rl.LoadModelAnimations(walkPath) if len(walkAnims) > 0 { animSet.Walk = walkAnims rl.TraceLog(rl.LogInfo, "Loaded walk animation: %s (%d frames, %f seconds)", walkPath, walkAnims[0].FrameCount, float32(walkAnims[0].FrameCount)/60.0) } } return animSet, nil } func LoadModels() ([]types.ModelAsset, error) { logging.Info.Println("Loading models") assetMutex.Lock() defer assetMutex.Unlock() if len(loadedModels) > 0 { logging.Info.Println("Returning cached models") models := make([]types.ModelAsset, 0, len(loadedModels)) for _, model := range loadedModels { models = append(models, model) } return models, nil } // Goonion model and animations goonerModel := rl.LoadModel("resources/models/gooner/walk_no_y_transform.glb") goonerAnims, err := loadModelAnimations(map[string]string{ "idle": "resources/models/gooner/idle_no_y_transform.glb", "walk": "resources/models/gooner/walk_no_y_transform.glb", }) if err != nil { return nil, err } // Apply transformations transform := rl.MatrixIdentity() transform = rl.MatrixMultiply(transform, rl.MatrixRotateY(180*rl.Deg2rad)) transform = rl.MatrixMultiply(transform, rl.MatrixRotateX(-90*rl.Deg2rad)) transform = rl.MatrixMultiply(transform, rl.MatrixScale(1.0, 1.0, 1.0)) goonerModel.Transform = transform // Coomer model (ready for animations) coomerModel := rl.LoadModel("resources/models/coomer/idle_notransy.glb") coomerAnims, err := loadModelAnimations(map[string]string{ "idle": "resources/models/coomer/idle_notransy.glb", "walk": "resources/models/coomer/unsteadywalk_notransy.glb", }) if err != nil { return nil, err } coomerModel.Transform = transform // Shreke model (ready for animations) shrekeModel := rl.LoadModel("resources/models/shreke/Animation_Slow_Orc_Walk_withSkin.glb") shrekeAnims, err := loadModelAnimations(map[string]string{ "idle": "resources/models/shreke/Animation_Slow_Orc_Walk_withSkin.glb", "walk": "resources/models/shreke/Animation_Excited_Walk_M_withSkin.glb", }) if err != nil { return nil, err } shrekeModel.Transform = transform // Store loaded models models := []types.ModelAsset{ { Name: "gooner", Model: goonerModel, Animation: append(goonerAnims.Idle, goonerAnims.Walk...), AnimFrames: int32(len(goonerAnims.Idle) + len(goonerAnims.Walk)), Animations: goonerAnims, YOffset: 0.0, }, { Model: coomerModel, Animation: append(coomerAnims.Idle, coomerAnims.Walk...), AnimFrames: int32(len(coomerAnims.Idle) + len(coomerAnims.Walk)), Animations: coomerAnims, YOffset: -4.0, }, { Model: shrekeModel, Animation: append(shrekeAnims.Idle, shrekeAnims.Walk...), AnimFrames: int32(len(shrekeAnims.Idle) + len(shrekeAnims.Walk)), Animations: shrekeAnims, YOffset: 0.0, }, } for _, model := range models { loadedModels[model.Name] = model } return models, nil } func LoadMusic(filename string) (rl.Music, error) { logging.Info.Printf("Loading music from %s", filename) audioMutex.Lock() defer audioMutex.Unlock() if !rl.IsAudioDeviceReady() { err := fmt.Errorf("audio device not initialized") logging.Error.Println(err) return rl.Music{}, err } music := rl.LoadMusicStream(filename) if music.CtxType == 0 { err := fmt.Errorf("failed to load music stream") logging.Error.Println(err) return rl.Music{}, err } logging.Info.Println("Music loaded successfully") return music, nil } func UnloadModels(models []types.ModelAsset) { assetMutex.Lock() defer assetMutex.Unlock() for _, model := range models { if model.Animation != nil { for i := int32(0); i < model.AnimFrames; i++ { rl.UnloadModelAnimation(model.Animation[i]) } } rl.UnloadModel(model.Model) rl.UnloadTexture(model.Texture) delete(loadedModels, model.Name) } } func UnloadMusic(music rl.Music) { rl.UnloadMusicStream(music) }