Example #1
0
        private void LoadEntitiesFromLump(Scene scene, LoadResult result, EntityLump entityLump, string layerName = null)
        {
            var childEntities = entityLump.GetChildEntityNames();

            foreach (var childEntityName in childEntities)
            {
                var newResource = guiContext.LoadFileByAnyMeansNecessary(childEntityName + "_c");

                if (newResource == null)
                {
                    continue;
                }

                var childLump = (EntityLump)newResource.DataBlock;
                var childName = childLump.Data.GetProperty <string>("m_name");

                LoadEntitiesFromLump(scene, result, childLump, childName);
            }

            var worldEntities = entityLump.GetEntities();

            foreach (var entity in worldEntities)
            {
                var classname = entity.GetProperty <string>("classname");

                if (classname == "info_world_layer")
                {
                    var spawnflags = entity.GetProperty <uint>("spawnflags");
                    var layername  = entity.GetProperty <string>("layername");

                    // Visible on spawn flag
                    if ((spawnflags & 1) == 1)
                    {
                        result.DefaultEnabledLayers.Add(layername);
                    }

                    continue;
                }
                else if (classname == "skybox_reference")
                {
                    var worldgroupid  = entity.GetProperty <string>("worldgroupid");
                    var targetmapname = entity.GetProperty <string>("targetmapname");

                    var skyboxWorldPath = $"maps/{Path.GetFileNameWithoutExtension(targetmapname)}/world.vwrld_c";
                    var skyboxPackage   = guiContext.LoadFileByAnyMeansNecessary(skyboxWorldPath);

                    if (skyboxPackage != null)
                    {
                        result.Skybox = (World)skyboxPackage.DataBlock;
                    }
                }

                var scale     = entity.GetProperty <string>("scales");
                var position  = entity.GetProperty <string>("origin");
                var angles    = entity.GetProperty <string>("angles");
                var model     = entity.GetProperty <string>("model");
                var skin      = entity.GetProperty <string>("skin");
                var colour    = entity.GetProperty <byte[]>("rendercolor");
                var particle  = entity.GetProperty <string>("effect_name");
                var animation = entity.GetProperty <string>("defaultanim");

                if (scale == null || position == null || angles == null)
                {
                    continue;
                }

                var isGlobalLight = classname == "env_global_light";
                var isCamera      =
                    classname == "sky_camera" ||
                    classname == "point_devshot_camera" ||
                    classname == "point_camera";

                var scaleMatrix = Matrix4x4.CreateScale(VectorExtensions.ParseVector(scale));

                var positionVector = VectorExtensions.ParseVector(position);
                var positionMatrix = Matrix4x4.CreateTranslation(positionVector);

                var pitchYawRoll = VectorExtensions.ParseVector(angles);
                var rollMatrix   = Matrix4x4.CreateRotationX(OpenTK.MathHelper.DegreesToRadians(pitchYawRoll.Z)); // Roll
                var pitchMatrix  = Matrix4x4.CreateRotationY(OpenTK.MathHelper.DegreesToRadians(pitchYawRoll.X)); // Pitch
                var yawMatrix    = Matrix4x4.CreateRotationZ(OpenTK.MathHelper.DegreesToRadians(pitchYawRoll.Y)); // Yaw

                var rotationMatrix       = rollMatrix * pitchMatrix * yawMatrix;
                var transformationMatrix = scaleMatrix * rotationMatrix * positionMatrix;

                if (classname == "sky_camera")
                {
                    result.SkyboxScale  = entity.GetProperty <ulong>("scale");
                    result.SkyboxOrigin = positionVector;
                }

                if (particle != null)
                {
                    var particleResource = guiContext.LoadFileByAnyMeansNecessary(particle + "_c");

                    if (particleResource != null)
                    {
                        var particleSystem = (ParticleSystem)particleResource.DataBlock;
                        var origin         = new Vector3(positionVector.X, positionVector.Y, positionVector.Z);

                        try
                        {
                            var particleNode = new ParticleSceneNode(scene, particleSystem)
                            {
                                Transform = Matrix4x4.CreateTranslation(origin),
                                LayerName = layerName,
                            };
                            scene.Add(particleNode, true);
                        }
                        catch (Exception e)
                        {
                            Console.Error.WriteLine($"Failed to setup particle '{particle}': {e.Message}");
                        }
                    }

                    continue;
                }

                if (isCamera)
                {
                    var name       = entity.GetProperty <string>("targetname") ?? string.Empty;
                    var cameraName = name == string.Empty
                        ? classname
                        : name;

                    result.CameraMatrices.Add(cameraName, transformationMatrix);

                    continue;
                }
                else if (isGlobalLight)
                {
                    result.GlobalLightPosition = positionVector;

                    continue;
                }
                else if (model == null)
                {
                    continue;
                }

                var objColor = Vector4.One;

                // Parse colour if present
                if (colour != default && colour.Length == 4)
                {
                    objColor.X = colour[0] / 255.0f;
                    objColor.Y = colour[1] / 255.0f;
                    objColor.Z = colour[2] / 255.0f;
                    objColor.W = colour[3] / 255.0f;
                }

                var newEntity = guiContext.LoadFileByAnyMeansNecessary(model + "_c");

                if (newEntity == null)
                {
                    var errorModelResource = guiContext.LoadFileByAnyMeansNecessary("models/dev/error.vmdl_c");

                    if (errorModelResource != null)
                    {
                        var errorModel = new ModelSceneNode(scene, (Model)errorModelResource.DataBlock, skin, false)
                        {
                            Transform = transformationMatrix,
                            LayerName = layerName,
                        };
                        scene.Add(errorModel, false);
                    }
                    else
                    {
                        Console.WriteLine("Unable to load error.vmdl_c. Did you add \"core/pak_001.dir\" to your game paths?");
                    }

                    continue;
                }

                var newModel = (Model)newEntity.DataBlock;

                var modelNode = new ModelSceneNode(scene, newModel, skin, false)
                {
                    Transform = transformationMatrix,
                    Tint      = objColor,
                    LayerName = layerName,
                };

                if (animation != default)
                {
                    modelNode.LoadAnimation(animation); // Load only this animation
                    modelNode.SetAnimation(animation);
                }

                var bodyHash = EntityLumpKeyLookup.Get("body");
                if (entity.Properties.ContainsKey(bodyHash))
                {
                    var groups    = modelNode.GetMeshGroups();
                    var body      = entity.Properties[bodyHash].Data;
                    int bodyGroup = -1;

                    if (body is ulong bodyGroupLong)
                    {
                        bodyGroup = (int)bodyGroupLong;
                    }
                    else if (body is string bodyGroupString)
                    {
                        int.TryParse(bodyGroupString, out bodyGroup);
                    }

                    modelNode.SetActiveMeshGroups(groups.Skip(bodyGroup).Take(1));
                }

                scene.Add(modelNode, false);
            }
        }
        private void LoadEntitiesFromLump(Scene scene, LoadResult result, EntityLump entityLump, string layerName = null)
        {
            var childEntities = entityLump.GetChildEntityNames();

            foreach (var childEntityName in childEntities)
            {
                var newResource = guiContext.LoadFileByAnyMeansNecessary(childEntityName + "_c");

                if (newResource == null)
                {
                    continue;
                }

                var childLump = (EntityLump)newResource.DataBlock;
                var childName = childLump.Data.GetProperty <string>("m_name");

                LoadEntitiesFromLump(scene, result, childLump, childName);
            }

            var worldEntities = entityLump.GetEntities();

            foreach (var entity in worldEntities)
            {
                var classname = entity.GetProperty <string>("classname");

                if (classname == "info_world_layer")
                {
                    var spawnflags = entity.GetProperty <uint>("spawnflags");
                    var layername  = entity.GetProperty <string>("layername");

                    // Visible on spawn flag
                    if ((spawnflags & 1) == 1)
                    {
                        result.DefaultEnabledLayers.Add(layername);
                    }

                    continue;
                }
                else if (classname == "skybox_reference")
                {
                    var worldgroupid  = entity.GetProperty <string>("worldgroupid");
                    var targetmapname = entity.GetProperty <string>("targetmapname");

                    var skyboxWorldPath = $"maps/{Path.GetFileNameWithoutExtension(targetmapname)}/world.vwrld_c";
                    var skyboxPackage   = guiContext.LoadFileByAnyMeansNecessary(skyboxWorldPath);

                    if (skyboxPackage != null)
                    {
                        result.Skybox = (World)skyboxPackage.DataBlock;
                    }
                }

                var scale    = entity.GetProperty <string>("scales");
                var position = entity.GetProperty <string>("origin");
                var angles   = entity.GetProperty <string>("angles");
                var model    = entity.GetProperty <string>("model");
                var skin     = entity.GetProperty <string>("skin");
                var particle = entity.GetProperty <string>("effect_name");
                //var animation = entity.GetProperty<string>("defaultanim");
                string animation = null;

                if (scale == null || position == null || angles == null)
                {
                    continue;
                }

                var isGlobalLight = classname == "env_global_light";
                var isCamera      =
                    classname == "sky_camera" ||
                    classname == "point_devshot_camera" ||
                    classname == "point_camera";
                var isTrigger =
                    classname.Contains("trigger") ||
                    classname == "post_processing_volume";

                var positionVector = EntityTransformHelper.ParseVector(position);

                var transformationMatrix = EntityTransformHelper.CalculateTransformationMatrix(entity);

                if (classname == "sky_camera")
                {
                    result.SkyboxScale  = entity.GetProperty <ulong>("scale");
                    result.SkyboxOrigin = positionVector;
                }

                if (particle != null)
                {
                    var particleResource = guiContext.LoadFileByAnyMeansNecessary(particle + "_c");

                    if (particleResource != null)
                    {
                        var particleSystem = (ParticleSystem)particleResource.DataBlock;
                        var origin         = new Vector3(positionVector.X, positionVector.Y, positionVector.Z);

                        try
                        {
                            var particleNode = new ParticleSceneNode(scene, particleSystem)
                            {
                                Transform = Matrix4x4.CreateTranslation(origin),
                                LayerName = layerName,
                            };
                            scene.Add(particleNode, true);
                        }
                        catch (Exception e)
                        {
                            Console.Error.WriteLine($"Failed to setup particle '{particle}': {e.Message}");
                        }
                    }

                    continue;
                }

                if (isCamera)
                {
                    var name       = entity.GetProperty <string>("targetname") ?? string.Empty;
                    var cameraName = string.IsNullOrEmpty(name)
                        ? classname
                        : name;

                    result.CameraMatrices.Add(cameraName, transformationMatrix);

                    continue;
                }
                else if (isGlobalLight)
                {
                    result.GlobalLightPosition = positionVector;

                    continue;
                }
                else if (model == null)
                {
                    continue;
                }

                var objColor = Vector4.One;

                // Parse colour if present
                var colour = entity.GetProperty("rendercolor");

                // HL Alyx has an entity that puts rendercolor as a string instead of color255
                // TODO: Make an enum for these types
                if (colour != default && colour.Type == 0x09)
                {
                    var colourBytes = (byte[])colour.Data;
                    objColor.X = colourBytes[0] / 255.0f;
                    objColor.Y = colourBytes[1] / 255.0f;
                    objColor.Z = colourBytes[2] / 255.0f;
                    objColor.W = colourBytes[3] / 255.0f;
                }

                var newEntity = guiContext.LoadFileByAnyMeansNecessary(model + "_c");

                if (newEntity == null)
                {
                    var errorModelResource = guiContext.LoadFileByAnyMeansNecessary("models/dev/error.vmdl_c");

                    if (errorModelResource != null)
                    {
                        var errorModel = new ModelSceneNode(scene, (Model)errorModelResource.DataBlock, skin, false)
                        {
                            Transform = transformationMatrix,
                            LayerName = layerName,
                        };
                        scene.Add(errorModel, false);
                    }

                    continue;
                }

                var newModel = (Model)newEntity.DataBlock;

                var modelNode = new ModelSceneNode(scene, newModel, skin, false)
                {
                    Transform = transformationMatrix,
                    Tint      = objColor,
                    LayerName = layerName,
                };

                if (animation != default)
                {
                    modelNode.LoadAnimation(animation); // Load only this animation
                    modelNode.SetAnimation(animation);
                }

                var bodyHash = StringToken.Get("body");
                if (entity.Properties.ContainsKey(bodyHash))
                {
                    var groups    = modelNode.GetMeshGroups();
                    var body      = entity.Properties[bodyHash].Data;
                    int bodyGroup = -1;

                    if (body is ulong bodyGroupLong)
                    {
                        bodyGroup = (int)bodyGroupLong;
                    }
                    else if (body is string bodyGroupString)
                    {
                        if (!int.TryParse(bodyGroupString, out bodyGroup))
                        {
                            bodyGroup = -1;
                        }
                    }

                    modelNode.SetActiveMeshGroups(groups.Skip(bodyGroup).Take(1));
                }

                scene.Add(modelNode, false);

                var phys = newModel.GetEmbeddedPhys();
                if (phys == null)
                {
                    var refPhysicsPaths = newModel.GetReferencedPhysNames();
                    if (refPhysicsPaths.Any())
                    {
                        var newResource = guiContext.LoadFileByAnyMeansNecessary(refPhysicsPaths.First() + "_c");
                        if (newResource != null)
                        {
                            phys = (PhysAggregateData)newResource.DataBlock;
                        }
                    }
                }

                if (phys != null)
                {
                    var physSceneNode = new PhysSceneNode(scene, phys)
                    {
                        Transform = transformationMatrix,
                        IsTrigger = isTrigger,
                        LayerName = layerName
                    };
                    scene.Add(physSceneNode, false);
                }
            }
        }