示例#1
0
 public void AddObject(ThnObject obj)
 {
     sceneObjects[obj.Name] = obj;
     if (obj.Object != null) {
         World?.AddObject(obj.Object);
     }
 }
示例#2
0
 private bool firstFrame = false; //Stops a desync in scene starting
 void RoomDoSceneScript(ThnScript sc, ScriptState state)
 {
     hotspots     = new List <RTCHotspot>();
     firstFrame   = true;
     currentState = state;
     if (sc == null)
     {
         currentState = ScriptState.None;
     }
     waitingForFinish = sc;
     scene.BeginScene(Scripts(sceneScripts, new[] { sc }));
     string[] ships = Array.Empty <string>();
     if (session.Ships != null)
     {
         ships = session.Ships.Select(x => Game.GameData.GetShip(x.ShipCRC).Nickname).ToArray();
     }
     for (int i = 0; (i < ships.Length && i < currentRoom.ForSaleShipPlacements.Count); i++)
     {
         ThnObject marker = scene.GetObject(currentRoom.ForSaleShipPlacements[i]);
         if (marker == null)
         {
             FLLog.Error("Base", "Couldn't display " + ships[i] + " on " + currentRoom.ForSaleShipPlacements[i]);
             continue;
         }
         var toSellShip = Game.GameData.GetShip(ships[i]);
         //Set up object
         var obj = new GameObject(toSellShip.ModelFile.LoadFile(Game.ResourceManager), Game.ResourceManager)
         {
             Parent = marker.Object
         };
         obj.PhysicsComponent = null;
         marker.Object.Children.Add(obj);
         if (obj.HardpointExists("HpMount"))
         {
             Matrix4x4.Invert(obj.GetHardpoint("HpMount").Transform, out var tr);
             obj.SetLocalTransform(tr);
         }
     }
     if (sc == null)
     {
         SetRoomCameraAndShip();
         letterboxAmount = -1;
         ui.Visible      = true;
     }
     else
     {
         ui.Visible      = false;
         letterboxAmount = 1;
     }
     if (cState == CutsceneState.Decision)
     {
         letterboxAmount = -1;
     }
 }
示例#3
0
 public ThnSound(string soundname, SoundManager man, ThnAudioProps props, ThnObject obj)
 {
     Object    = obj;
     this.man  = man;
     SoundName = soundname;
     man.LoadSound(soundname);
     Props = props;
     if (Props != null)
     {
         Attenuation = props.Attenuation;
     }
 }
示例#4
0
        public Cutscene(ThnScriptContext context, GameDataManager gameData, Viewport viewport, Game game)
        {
            this.game     = game;
            this.gameData = gameData;
            scriptContext = context;
            camera        = new ThnCamera(viewport);

            Renderer = new SystemRenderer(camera, gameData, game.GetService <GameResourceManager>(), game);
            World    = new GameWorld(Renderer, false);
            //thn = script;
            var evs = new List <ThnEvent>();

            foreach (var thn in context.Scripts)
            {
                totalDuration = Math.Max(totalDuration, thn.Duration);
                foreach (var ev in thn.Events)
                {
                    ev.TimeOffset = 0;
                    evs.Add(ev);
                }
                AddEntities(thn);
            }
            //work around SET_CAMERA not being called in disco (match vanilla behaviour)
            var firstCamera = Objects.Values.Where(x => x.Camera != null).FirstOrDefault();

            if (firstCamera != null)
            {
                camera.Transform = firstCamera.Camera;
            }
            evs.Sort((x, y) => x.Time.CompareTo(y.Time));
            foreach (var item in evs)
            {
                events.Enqueue(item);
            }
            //Add starspheres in the right order
            var sorted = ((IEnumerable <Tuple <IDrawable, ThnObject> >)layers).Reverse().OrderBy(x => x.Item2.Entity.SortGroup).ToArray();

            Renderer.StarSphereModels    = new RigidModel[sorted.Length];
            Renderer.StarSphereWorlds    = new Matrix4x4[sorted.Length];
            Renderer.StarSphereLightings = new Lighting[sorted.Length];
            starSphereObjects            = new ThnObject[sorted.Length];
            for (int i = 0; i < sorted.Length; i++)
            {
                Renderer.StarSphereModels[i]    = (sorted[i].Item1 as IRigidModelFile).CreateRigidModel(true);
                Renderer.StarSphereWorlds[i]    = sorted[i].Item2.Rotate * Matrix4x4.CreateTranslation(sorted[i].Item2.Translate);
                Renderer.StarSphereLightings[i] = Lighting.Empty;
                starSphereObjects[i]            = sorted[i].Item2;
            }
            //Add objects to the renderer
            World.RegisterAll();
        }
示例#5
0
        void ProcessNextCutscene()
        {
            var ct       = toPlay.Dequeue();
            int position = 0;
            int i        = 0;

            foreach (var npc in ct.Chars)
            {
                var obj = new GameObject()
                {
                    Nickname = npc.Actor
                };
                var costumeName = Game.GameData.GetCostumeForNPC(npc.Npc);
                Game.GameData.GetCostume(costumeName, out var body, out var head, out var lh, out var rh);
                var skel = new DfmSkeletonManager(body, head, lh, rh);
                obj.RenderComponent = new CharacterRenderer(skel);
                var anmComponent = new AnimationComponent(obj, Game.GameData.GetCharacterAnimations());
                obj.AnimationComponent = anmComponent;
                obj.Components.Add(anmComponent);
                string spot = npc.Spot;
                if (string.IsNullOrEmpty(spot))
                {
                    spot = ct.Reserves[0].Spot[position++];
                }
                var pos = scene.GetObject(spot).Translate;
                obj.SetLocalTransform(Matrix4x4.CreateTranslation(pos));
                var thnObj = new ThnObject();
                thnObj.Name      = npc.Actor;
                thnObj.Rotate    = Matrix4x4.Identity;
                thnObj.Translate = pos;
                thnObj.Object    = obj;
                scene.AddObject(thnObj);
                scene.FidgetScript(new ThnScript(session.Game.GameData.ResolveDataPath(npc.Fidget)));
                if (i == 0)
                {
                    hotspots.Add(new RTCHotspot()
                    {
                        ini = ct, obj = thnObj, npc = npc.Npc
                    });
                }
                i++;
            }
        }
示例#6
0
        void SetRoomCameraAndShip()
        {
            if (currentRoom.Camera != null)
            {
                scene.SetCamera(currentRoom.Camera);
            }
            ThnObject shipMarker = scene.GetObject(currentRoom.PlayerShipPlacement);

            if (shipMarker != null)
            {
                if (playerShip.HardpointExists("HpMount"))
                {
                    Matrix4x4.Invert(playerShip.GetHardpoint("HpMount").Transform, out var tr);
                    playerShip.SetLocalTransform(tr);
                }
                else
                {
                    playerShip.SetLocalTransform(Matrix4x4.Identity);
                }
                shipMarker.Object.Children.Add(playerShip);
            }
        }
示例#7
0
        public Cutscene(IEnumerable <ThnScript> scripts, FreelancerGame game)
        {
            camera = new ThnCamera(game.Viewport);

            Renderer = new SystemRenderer(camera, game.GameData, game.ResourceManager);
            World    = new GameWorld(Renderer);

            //thn = script;
            var  evs      = new List <ThnEvent>();
            bool hasScene = false;
            List <Tuple <IDrawable, Matrix4, int> > layers = new List <Tuple <IDrawable, Matrix4, int> >();

            foreach (var thn in scripts)
            {
                foreach (var ev in thn.Events)
                {
                    evs.Add(ev);
                }
                foreach (var kv in thn.Entities)
                {
                    if ((kv.Value.ObjectFlags & ThnObjectFlags.Reference) == ThnObjectFlags.Reference)
                    {
                        continue;
                    }
                    var obj = new ThnObject();
                    obj.Name      = kv.Key;
                    obj.Translate = kv.Value.Position ?? Vector3.Zero;
                    obj.Rotate    = kv.Value.RotationMatrix ?? Matrix4.Identity;
                    if (kv.Value.Type == EntityTypes.Compound)
                    {
                        //Fetch model
                        IDrawable drawable;
                        switch (kv.Value.MeshCategory.ToLowerInvariant())
                        {
                        case "solar":
                            drawable = game.GameData.GetSolar(kv.Value.Template);
                            break;

                        case "spaceship":
                            var sh = game.GameData.GetShip(kv.Value.Template);
                            drawable = sh.Drawable;
                            break;

                        case "prop":
                            drawable = game.GameData.GetProp(kv.Value.Template);
                            break;

                        case "room":
                            drawable = game.GameData.GetRoom(kv.Value.Template);
                            break;

                        case "equipment cart":
                            drawable = game.GameData.GetCart(kv.Value.Template);
                            break;

                        case "equipment":
                            var eq = game.GameData.GetEquipment(kv.Value.Template);
                            drawable = eq.GetDrawable();
                            break;

                        case "asteroid":
                            drawable = game.GameData.GetAsteroid(kv.Value.Template);
                            break;

                        default:
                            throw new NotImplementedException("Mesh Category " + kv.Value.MeshCategory);
                        }
                        if (kv.Value.UserFlag != 0)
                        {
                            //This is a starsphere
                            var transform = (kv.Value.RotationMatrix ?? Matrix4.Identity) * Matrix4.CreateTranslation(kv.Value.Position ?? Vector3.Zero);
                            layers.Add(new Tuple <IDrawable, Matrix4, int>(drawable, transform, kv.Value.SortGroup));
                        }
                        else
                        {
                            obj.Object = new GameObject(drawable, game.ResourceManager, false);
                            obj.Object.PhysicsComponent = null;                             //Jitter seems to interfere with directly setting orientation
                            var r = (ModelRenderer)obj.Object.RenderComponent;
                            r.LightGroup = kv.Value.LightGroup;
                            r.LitDynamic = (kv.Value.ObjectFlags & ThnObjectFlags.LitDynamic) == ThnObjectFlags.LitDynamic;
                            r.LitAmbient = (kv.Value.ObjectFlags & ThnObjectFlags.LitAmbient) == ThnObjectFlags.LitAmbient;
                            //HIDDEN just seems to be an editor flag?
                            //r.Hidden = (kv.Value.ObjectFlags & ThnObjectFlags.Hidden) == ThnObjectFlags.Hidden;
                            r.NoFog = kv.Value.NoFog;
                        }
                    }
                    else if (kv.Value.Type == EntityTypes.PSys)
                    {
                        var fx = game.GameData.GetEffect(kv.Value.Template);
                        obj.Object = new GameObject();
                        obj.Object.RenderComponent = new ParticleEffectRenderer(fx)
                        {
                            Active = false
                        };
                    }
                    else if (kv.Value.Type == EntityTypes.Scene)
                    {
                        if (hasScene)
                        {
                            //throw new Exception("Thn can only have one scene");
                            //TODO: This needs to be handled better
                            continue;
                        }
                        var amb = kv.Value.Ambient.Value;
                        if (amb.X == 0 && amb.Y == 0 && amb.Z == 0)
                        {
                            continue;
                        }
                        hasScene = true;
                        Renderer.SystemLighting.Ambient = new Color4(amb.X / 255f, amb.Y / 255f, amb.Z / 255f, 1);
                    }
                    else if (kv.Value.Type == EntityTypes.Light)
                    {
                        var lt = new DynamicLight();
                        lt.LightGroup = kv.Value.LightGroup;
                        lt.Active     = kv.Value.LightProps.On;
                        lt.Light      = kv.Value.LightProps.Render;
                        obj.Light     = lt;
                        if (kv.Value.RotationMatrix.HasValue)
                        {
                            var m = kv.Value.RotationMatrix.Value;
                            lt.Light.Direction = (new Vector4(lt.Light.Direction.Normalized(), 0) * m).Xyz.Normalized();
                        }
                        Renderer.SystemLighting.Lights.Add(lt);
                    }
                    else if (kv.Value.Type == EntityTypes.Camera)
                    {
                        obj.Camera             = new ThnCameraTransform();
                        obj.Camera.Position    = kv.Value.Position.Value;
                        obj.Camera.Orientation = kv.Value.RotationMatrix ?? Matrix4.Identity;
                        obj.Camera.FovH        = kv.Value.FovH ?? obj.Camera.FovH;
                        obj.Camera.AspectRatio = kv.Value.HVAspect ?? obj.Camera.AspectRatio;
                    }
                    else if (kv.Value.Type == EntityTypes.Marker)
                    {
                        obj.Object          = new GameObject();
                        obj.Object.Name     = "Marker";
                        obj.Object.Nickname = "";
                    }
                    if (obj.Object != null)
                    {
                        Vector3 transform = kv.Value.Position ?? Vector3.Zero;
                        obj.Object.Transform = (kv.Value.RotationMatrix ?? Matrix4.Identity) * Matrix4.CreateTranslation(transform);
                        World.Objects.Add(obj.Object);
                    }
                    obj.Entity = kv.Value;
                    Objects.Add(kv.Key, obj);
                }
            }
            evs.Sort((x, y) => x.Time.CompareTo(y.Time));
            foreach (var item in evs)
            {
                events.Enqueue(item);
            }
            //Add starspheres in the right order
            layers.Sort((x, y) => x.Item3.CompareTo(y.Item3));
            Renderer.StarSphereModels = new IDrawable[layers.Count];
            Renderer.StarSphereWorlds = new Matrix4[layers.Count];
            for (int i = 0; i < layers.Count; i++)
            {
                Renderer.StarSphereModels[i] = layers[i].Item1;
                Renderer.StarSphereWorlds[i] = layers[i].Item2;
            }
            //Add objects to the renderer
            World.RegisterAll();
        }
示例#8
0
        void AddEntities(ThnScript thn)
        {
            foreach (var kv in thn.Entities)
            {
                if (Objects.ContainsKey(kv.Key))
                {
                    continue;
                }
                if ((kv.Value.ObjectFlags & ThnObjectFlags.Reference) == ThnObjectFlags.Reference)
                {
                    continue;
                }
                var obj = new ThnObject();
                obj.Name      = kv.Key;
                obj.Translate = kv.Value.Position ?? Vector3.Zero;
                obj.Rotate    = kv.Value.RotationMatrix ?? Matrix4.Identity;
                //PlayerShip object
                if (spawnObjects && scriptContext.PlayerShip != null && kv.Value.Type == EntityTypes.Compound &&
                    kv.Value.Template.Equals("playership", StringComparison.InvariantCultureIgnoreCase))
                {
                    obj.Object = scriptContext.PlayerShip;
                    obj.Object.RenderComponent.LitDynamic = (kv.Value.ObjectFlags & ThnObjectFlags.LitDynamic) == ThnObjectFlags.LitDynamic;
                    obj.Object.RenderComponent.LitAmbient = (kv.Value.ObjectFlags & ThnObjectFlags.LitAmbient) == ThnObjectFlags.LitAmbient;
                    obj.Object.RenderComponent.NoFog      = kv.Value.NoFog;
                    ((ModelRenderer)obj.Object.RenderComponent).LightGroup = kv.Value.LightGroup;
                    obj.Entity = kv.Value;
                    Vector3 transform = kv.Value.Position ?? Vector3.Zero;
                    obj.Object.Transform = (kv.Value.RotationMatrix ?? Matrix4.Identity) * Matrix4.CreateTranslation(transform);
                    obj.HpMount          = scriptContext.PlayerShip.GetHardpoint("HpMount");
                    World.Objects.Add(obj.Object);
                    Objects.Add(kv.Key, obj);
                    continue;
                }

                var    template = kv.Value.Template;
                string replacement;
                if (scriptContext != null &&
                    scriptContext.Substitutions.TryGetValue(kv.Value.Template, out replacement))
                {
                    template = replacement;
                }
                if (spawnObjects && kv.Value.Type == EntityTypes.Compound)
                {
                    bool getHpMount = false;
                    //Fetch model
                    IDrawable drawable;
                    switch (kv.Value.MeshCategory.ToLowerInvariant())
                    {
                    case "solar":
                        drawable = gameData.GetSolar(template);
                        break;

                    case "ship":
                    case "spaceship":
                        getHpMount = true;
                        var sh = gameData.GetShip(template);
                        sh.LoadResources();
                        drawable = sh.Drawable;
                        break;

                    case "prop":
                        drawable = gameData.GetProp(template);
                        break;

                    case "room":
                        drawable = gameData.GetRoom(template);
                        break;

                    case "equipment cart":
                        drawable = gameData.GetCart(template);
                        break;

                    case "equipment":
                        var eq = gameData.GetEquipment(template);
                        eq.LoadResources();
                        drawable = eq.GetDrawable();
                        break;

                    case "asteroid":
                        drawable = gameData.GetAsteroid(kv.Value.Template);
                        break;

                    default:
                        throw new NotImplementedException("Mesh Category " + kv.Value.MeshCategory);
                    }
                    if (kv.Value.UserFlag != 0)
                    {
                        //This is a starsphere
                        var transform = (kv.Value.RotationMatrix ?? Matrix4.Identity) * Matrix4.CreateTranslation(kv.Value.Position ?? Vector3.Zero);
                        layers.Add(new Tuple <IDrawable, Matrix4, int>(drawable, transform, kv.Value.SortGroup));
                    }
                    else
                    {
                        obj.Object                  = new GameObject(drawable, game.GetService <ResourceManager>(), true, false);
                        obj.Object.Name             = kv.Value.Name;
                        obj.Object.PhysicsComponent = null; //Jitter seems to interfere with directly setting orientation
                        if (getHpMount)
                        {
                            obj.HpMount = obj.Object.GetHardpoint("HpMount");
                        }
                        var r = (ModelRenderer)obj.Object.RenderComponent;
                        r.LightGroup = kv.Value.LightGroup;
                        r.LitDynamic = (kv.Value.ObjectFlags & ThnObjectFlags.LitDynamic) == ThnObjectFlags.LitDynamic;
                        r.LitAmbient = (kv.Value.ObjectFlags & ThnObjectFlags.LitAmbient) == ThnObjectFlags.LitAmbient;
                        //HIDDEN just seems to be an editor flag?
                        //r.Hidden = (kv.Value.ObjectFlags & ThnObjectFlags.Hidden) == ThnObjectFlags.Hidden;
                        r.NoFog = kv.Value.NoFog;
                    }
                }
                else if (kv.Value.Type == EntityTypes.PSys)
                {
                    var fx = gameData.GetEffect(kv.Value.Template);
                    obj.Object = new GameObject();
                    obj.Object.RenderComponent = new ParticleEffectRenderer(fx)
                    {
                        Active = false
                    };
                }
                else if (kv.Value.Type == EntityTypes.Scene)
                {
                    if (hasScene)
                    {
                        //throw new Exception("Thn can only have one scene");
                        //TODO: This needs to be handled better
                        continue;
                    }
                    var amb = kv.Value.Ambient.Value;
                    if (amb.X == 0 && amb.Y == 0 && amb.Z == 0)
                    {
                        continue;
                    }
                    hasScene = true;
                    Renderer.SystemLighting.Ambient = new Color4(amb.X / 255f, amb.Y / 255f, amb.Z / 255f, 1);
                }
                else if (kv.Value.Type == EntityTypes.Light)
                {
                    var lt = new DynamicLight();
                    lt.LightGroup = kv.Value.LightGroup;
                    lt.Active     = kv.Value.LightProps.On;
                    lt.Light      = kv.Value.LightProps.Render;
                    obj.Light     = lt;
                    obj.LightDir  = lt.Light.Direction;
                    if (kv.Value.RotationMatrix.HasValue)
                    {
                        var m = kv.Value.RotationMatrix.Value;
                        lt.Light.Direction = (new Vector4(lt.Light.Direction.Normalized(), 0) * m).Xyz.Normalized();
                    }
                    if (Renderer != null)
                    {
                        Renderer.SystemLighting.Lights.Add(lt);
                    }
                }
                else if (kv.Value.Type == EntityTypes.Camera)
                {
                    obj.Camera             = new ThnCameraTransform();
                    obj.Camera.Position    = kv.Value.Position.Value;
                    obj.Camera.Orientation = kv.Value.RotationMatrix ?? Matrix4.Identity;
                    obj.Camera.FovH        = kv.Value.FovH ?? obj.Camera.FovH;
                    obj.Camera.AspectRatio = kv.Value.HVAspect ?? obj.Camera.AspectRatio;
                }
                else if (kv.Value.Type == EntityTypes.Marker)
                {
                    obj.Object          = new GameObject();
                    obj.Object.Name     = "Marker";
                    obj.Object.Nickname = "";
                }
                else if (kv.Value.Type == EntityTypes.Sound)
                {
                    obj.Sound         = new ThnSound(kv.Value.Template, game.GetService <SoundManager>(), kv.Value.AudioProps, obj);
                    obj.Sound.Spatial = (kv.Value.ObjectFlags & ThnObjectFlags.Spatial) == ThnObjectFlags.Spatial;
                }
                if (obj.Object != null)
                {
                    Vector3 transform = kv.Value.Position ?? Vector3.Zero;
                    obj.Object.Transform = (kv.Value.RotationMatrix ?? Matrix4.Identity) * Matrix4.CreateTranslation(transform);
                    World.Objects.Add(obj.Object);
                }
                obj.Entity      = kv.Value;
                Objects[kv.Key] = obj;
            }
        }
示例#9
0
 public ThnSoundInstance(ThnSound snd, Media.SoundInstance ms)
 {
     Object   = snd.Object;
     Spatial  = snd.Spatial;
     Instance = ms;
 }
示例#10
0
        void SceneSetup(ThnScript[] scripts, bool resetObjects = true)
        {
            hasScene = false;
            currentTime = 0;
            if (resetObjects)
            {
                sceneObjects = new Dictionary<string, ThnObject>(StringComparer.OrdinalIgnoreCase);
                layers = new List<Tuple<IDrawable, ThnObject>>();
            }

            if (spawnObjects && resetObjects) {
                if (Renderer != null)
                {
                    Renderer.Dispose();
                    World.Dispose();
                }
                Renderer = new SystemRenderer(camera, gameData, game.GetService<GameResourceManager>(), game);
                World = new GameWorld(Renderer, false);
            }
            if (scriptContext.SetScript != null && resetObjects)
            {
                var inst = new ThnScriptInstance(this, scriptContext.SetScript);
                inst.ConstructEntities(sceneObjects, spawnObjects);
            }
            if (instances != null && resetObjects)
            {
                foreach (var inst in instances)
                    inst.Cleanup();
            }
            if(resetObjects)
                instances = new List<ThnScriptInstance>();
            foreach (var script in scripts)
            {
                var ts = new ThnScriptInstance(this, script);
                ts.ConstructEntities(sceneObjects, spawnObjects && resetObjects);
                instances.Add(ts);
            }

            if (resetObjects)
            {
                var firstCamera = sceneObjects.Values.FirstOrDefault(x => x.Camera != null);
                if (firstCamera == null) firstCamera = sceneObjects.Values.FirstOrDefault(x => x.Camera != null);
                if (firstCamera != null)
                {
                    camera.Transform = firstCamera.Camera;
                }
            }

            if (spawnObjects && resetObjects)
            {
                //Add starspheres in the right order
                var sorted = ((IEnumerable<Tuple<IDrawable, ThnObject>>) layers).Reverse()
                    .OrderBy(x => x.Item2.Entity.SortGroup).ToArray();
                Renderer.StarSphereModels = new RigidModel[sorted.Length];
                Renderer.StarSphereWorlds = new Matrix4x4[sorted.Length];
                Renderer.StarSphereLightings = new Lighting[sorted.Length];
                starSphereObjects = new ThnObject[sorted.Length];
                for (int i = 0; i < sorted.Length; i++)
                {
                    Renderer.StarSphereModels[i] = (sorted[i].Item1 as IRigidModelFile).CreateRigidModel(true);
                    Renderer.StarSphereWorlds[i] =
                        sorted[i].Item2.Rotate * Matrix4x4.CreateTranslation(sorted[i].Item2.Translate);
                    Renderer.StarSphereLightings[i] = Lighting.Empty;
                    starSphereObjects[i] = sorted[i].Item2;
                }
                //Add objects to the renderer
                World.RegisterAll();
            }

            lagCounter = 0;
            //Init
            _Update(0);
            running = true;
        }
示例#11
0
 public void AddStarsphere(IDrawable drawable, ThnObject obj)
 {
     layers.Add(new Tuple<IDrawable, ThnObject>(drawable, obj));
 }
示例#12
0
        public void ConstructEntities(Dictionary <string, ThnObject> objects, bool spawnObjects)
        {
            this.Objects = objects;
            foreach (var kv in thn.Entities)
            {
                if (Objects.ContainsKey(kv.Key))
                {
                    continue;
                }
                if ((kv.Value.ObjectFlags & ThnObjectFlags.Reference) == ThnObjectFlags.Reference)
                {
                    continue;
                }
                var obj = new ThnObject();
                obj.Name      = kv.Key;
                obj.Translate = kv.Value.Position ?? Vector3.Zero;
                obj.Rotate    = kv.Value.RotationMatrix ?? Matrix4x4.Identity;
                //PlayerShip object
                if (spawnObjects && Cutscene.PlayerShip != null && kv.Value.Type == EntityTypes.Compound &&
                    kv.Value.Template.Equals("playership", StringComparison.InvariantCultureIgnoreCase))
                {
                    obj.Object = Cutscene.PlayerShip;
                    obj.Object.RenderComponent.LitDynamic = (kv.Value.ObjectFlags & ThnObjectFlags.LitDynamic) == ThnObjectFlags.LitDynamic;
                    obj.Object.RenderComponent.LitAmbient = (kv.Value.ObjectFlags & ThnObjectFlags.LitAmbient) == ThnObjectFlags.LitAmbient;
                    obj.Object.RenderComponent.NoFog      = kv.Value.NoFog;
                    ((ModelRenderer)obj.Object.RenderComponent).LightGroup = kv.Value.LightGroup;
                    obj.Entity = kv.Value;
                    Vector3 transform = kv.Value.Position ?? Vector3.Zero;
                    obj.Object.SetLocalTransform((kv.Value.RotationMatrix ?? Matrix4x4.Identity) *
                                                 Matrix4x4.CreateTranslation(transform));
                    obj.HpMount = Cutscene.PlayerShip.GetHardpoint("HpMount");
                    Cutscene.World.AddObject(obj.Object);
                    Objects.Add(kv.Key, obj);
                    continue;
                }

                var    template = kv.Value.Template;
                string replacement;
                if (Cutscene.Substitutions != null &&
                    Cutscene.Substitutions.TryGetValue(kv.Value.Template, out replacement))
                {
                    template = replacement;
                }
                var resman   = Cutscene.Game.GetService <ResourceManager>();
                var gameData = Cutscene.GameData;
                if (spawnObjects && kv.Value.Type == EntityTypes.Compound)
                {
                    bool getHpMount = false;
                    //Fetch model
                    IDrawable drawable  = null;
                    float[]   lodranges = null;
                    if (!string.IsNullOrEmpty(template))
                    {
                        switch (kv.Value.MeshCategory.ToLowerInvariant())
                        {
                        case "solar":
                            (drawable, lodranges) = gameData.GetSolar(template);
                            break;

                        case "ship":
                        case "spaceship":
                            getHpMount = true;
                            var sh = gameData.GetShip(template);
                            drawable = sh.ModelFile.LoadFile(resman);
                            break;

                        case "prop":
                            drawable = gameData.GetProp(template);
                            break;

                        case "room":
                            drawable = gameData.GetRoom(template);
                            break;

                        case "equipment cart":
                            drawable = gameData.GetCart(template);
                            break;

                        case "equipment":
                            var eq = gameData.GetEquipment(template);
                            drawable = eq?.ModelFile.LoadFile(resman);
                            break;

                        case "asteroid":
                            drawable = gameData.GetAsteroid(kv.Value.Template);
                            break;

                        default:
                            throw new NotImplementedException("Mesh Category " + kv.Value.MeshCategory);
                        }
                    }
                    else
                    {
                        FLLog.Warning("Thn", $"object '{kv.Value.Name}' has empty template, category " +
                                      $"'{kv.Value.MeshCategory}'");
                    }

                    drawable?.Initialize(resman);
                    if (kv.Value.UserFlag != 0)
                    {
                        //This is a starsphere
                        Cutscene.AddStarsphere(drawable, obj);
                    }
                    else
                    {
                        obj.Object                  = new GameObject(drawable, Cutscene.Game.GetService <ResourceManager>(), true, false);
                        obj.Object.Name             = kv.Value.Name;
                        obj.Object.PhysicsComponent = null; //Jitter seems to interfere with directly setting orientation
                        if (getHpMount)
                        {
                            obj.HpMount = obj.Object.GetHardpoint("HpMount");
                        }
                        var r = (ModelRenderer)obj.Object.RenderComponent;
                        if (r != null)
                        {
                            r.LightGroup = kv.Value.LightGroup;
                            r.LitDynamic = (kv.Value.ObjectFlags & ThnObjectFlags.LitDynamic) ==
                                           ThnObjectFlags.LitDynamic;
                            r.LitAmbient = (kv.Value.ObjectFlags & ThnObjectFlags.LitAmbient) ==
                                           ThnObjectFlags.LitAmbient;
                            //HIDDEN just seems to be an editor flag?
                            //r.Hidden = (kv.Value.ObjectFlags & ThnObjectFlags.Hidden) == ThnObjectFlags.Hidden;
                            r.NoFog     = kv.Value.NoFog;
                            r.LODRanges = lodranges;
                        }
                    }
                }
                else if (kv.Value.Type == EntityTypes.PSys)
                {
                    var fx = gameData.GetEffect(kv.Value.Template);
                    if (fx != null)
                    {
                        obj.Object = new GameObject();
                        obj.Object.RenderComponent = new ParticleEffectRenderer(fx.GetEffect(resman))
                        {
                            Active = false
                        };
                    }
                }
                else if (kv.Value.Type == EntityTypes.Scene)
                {
                    if (kv.Value.DisplayText != null)
                    {
                        Cutscene.SetDisplayText(kv.Value.DisplayText);
                    }

                    var amb = kv.Value.Ambient.Value;
                    if (amb.X == 0 && amb.Y == 0 && amb.Z == 0)
                    {
                        continue;
                    }
                    Cutscene.SetAmbient(amb);
                }
                else if (kv.Value.Type == EntityTypes.Light)
                {
                    var lt = new DynamicLight();
                    lt.LightGroup = kv.Value.LightGroup;
                    lt.Active     = kv.Value.LightProps.On;
                    lt.Light      = kv.Value.LightProps.Render;
                    obj.Light     = lt;
                    obj.LightDir  = lt.Light.Direction;
                    if (kv.Value.RotationMatrix.HasValue)
                    {
                        var m = kv.Value.RotationMatrix.Value;
                        lt.Light.Direction = Vector3.TransformNormal(lt.Light.Direction, m);
                    }
                    if (Cutscene.Renderer != null)
                    {
                        Cutscene.Renderer.SystemLighting.Lights.Add(lt);
                    }
                }
                else if (kv.Value.Type == EntityTypes.Camera)
                {
                    obj.Camera             = new ThnCameraTransform();
                    obj.Camera.Position    = kv.Value.Position.Value;
                    obj.Camera.Orientation = kv.Value.RotationMatrix ?? Matrix4x4.Identity;
                    obj.Camera.FovH        = kv.Value.FovH ?? obj.Camera.FovH;
                    obj.Camera.AspectRatio = kv.Value.HVAspect ?? obj.Camera.AspectRatio;
                    if (kv.Value.NearPlane != null)
                    {
                        obj.Camera.Znear = kv.Value.NearPlane.Value;
                    }
                    if (kv.Value.FarPlane != null)
                    {
                        obj.Camera.Zfar = kv.Value.FarPlane.Value;
                    }
                }
                else if (kv.Value.Type == EntityTypes.Marker)
                {
                    obj.Object          = new GameObject();
                    obj.Object.Name     = "Marker";
                    obj.Object.Nickname = "";
                    if (kv.Value.MainObject && Cutscene.MainObject != null)
                    {
                        obj.Object.Parent = Cutscene.MainObject;
                        obj.Object.Components.Add(new DirtyTransformComponent(obj.Object));
                        obj.PosFromObject = true;
                    }
                }
                else if (kv.Value.Type == EntityTypes.Deformable)
                {
                    //TODO: Hacky with fidget/placement scripts
                    if (string.IsNullOrEmpty(kv.Value.Actor) || !objects.ContainsKey(kv.Value.Actor))
                    {
                        obj.Object = new GameObject();
                        gameData.GetCostume(template, out DfmFile body, out DfmFile head, out DfmFile leftHand,
                                            out DfmFile rightHand);
                        var skel = new DfmSkeletonManager(body, head, leftHand, rightHand);
                        obj.Object.RenderComponent = new CharacterRenderer(skel);
                        var anmComponent = new AnimationComponent(obj.Object, gameData.GetCharacterAnimations());
                        obj.Object.AnimationComponent = anmComponent;
                        obj.Object.Components.Add(anmComponent);
                    }
                    else
                    {
                        obj.Actor = kv.Value.Actor;
                        if (Objects.TryGetValue(obj.Actor, out var act))
                        {
                            act.Translate = obj.Translate;
                            act.Rotate    = obj.Rotate;
                            act.Update();
                        }
                    }
                }
                else if (kv.Value.Type == EntityTypes.Sound)
                {
                    obj.Sound         = new ThnSound(kv.Value.Template, Cutscene.Game.GetService <SoundManager>(), kv.Value.AudioProps, obj);
                    obj.Sound.Spatial = (kv.Value.ObjectFlags & ThnObjectFlags.SoundSpatial) == ThnObjectFlags.SoundSpatial;
                }
                if (obj.Object != null)
                {
                    if (!obj.PosFromObject)
                    {
                        Vector3 transform = kv.Value.Position ?? Vector3.Zero;
                        obj.Object.SetLocalTransform((kv.Value.RotationMatrix ?? Matrix4x4.Identity) *
                                                     Matrix4x4.CreateTranslation(transform));
                    }
                    Cutscene.World.AddObject(obj.Object);
                }
                obj.Entity      = kv.Value;
                Objects[kv.Key] = obj;
            }
        }