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); } } }
protected override void LoadScene() { if (model != null) { modelSceneNode = new ModelSceneNode(Scene, model); SetAvailableAnimations(modelSceneNode.GetSupportedAnimationNames()); Scene.Add(modelSceneNode, false); phys = model.GetEmbeddedPhys(); if (phys == null) { var refPhysicsPaths = model.GetReferencedPhysNames(); if (refPhysicsPaths.Any()) { //TODO are there any models with more than one vphys? if (refPhysicsPaths.Count() != 1) { Console.WriteLine($"Model has more than 1 vphys ({refPhysicsPaths.Count()})." + " Please report this on https://github.com/SteamDatabase/ValveResourceFormat and provide the file that caused this."); } var newResource = Scene.GuiContext.LoadFileByAnyMeansNecessary(refPhysicsPaths.First() + "_c"); if (newResource != null) { phys = (PhysAggregateData)newResource.DataBlock; } } } var meshGroups = modelSceneNode.GetMeshGroups(); if (meshGroups.Count() > 1) { meshGroupListBox = ViewerControl.AddMultiSelection("Mesh Group", selectedGroups => { modelSceneNode.SetActiveMeshGroups(selectedGroups); }); meshGroupListBox.Items.AddRange(modelSceneNode.GetMeshGroups().ToArray <object>()); foreach (var group in modelSceneNode.GetActiveMeshGroups()) { meshGroupListBox.SetItemChecked(meshGroupListBox.FindStringExact(group), true); } } var materialGroups = model.GetMaterialGroups(); if (materialGroups.Count() > 1) { materialGroupListBox = ViewerControl.AddSelection("Material Group", (selectedGroup, _) => { modelSceneNode?.SetSkin(selectedGroup); }); materialGroupListBox.Items.AddRange(materialGroups.ToArray <object>()); materialGroupListBox.SelectedIndex = 0; } modelSceneNode.AnimationController.RegisterUpdateHandler((animation, frame) => { if (animationTrackBar.TrackBar.Value != frame) { animationTrackBar.UpdateValueSilently(frame); } var maximum = animation == null ? 1 : animation.FrameCount - 1; if (animationTrackBar.TrackBar.Maximum != maximum) { animationTrackBar.TrackBar.Maximum = maximum; } animationTrackBar.Enabled = animation != null; animationPlayPause.Enabled = animation != null; }); } else { SetAvailableAnimations(Enumerable.Empty <string>()); } if (mesh != null) { meshSceneNode = new MeshSceneNode(Scene, mesh); Scene.Add(meshSceneNode, false); } if (phys != null) { physSceneNode = new PhysSceneNode(Scene, phys); Scene.Add(physSceneNode, false); //disabled by default. Enable if viewing only phys or model without meshes physSceneNode.Enabled = (modelSceneNode == null || !modelSceneNode.RenderableMeshes.Any()); ViewerControl.AddCheckBox("Show Physics", physSceneNode.Enabled, (v) => { physSceneNode.Enabled = v; }); } }