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); } } }