private static bool ReplaceMesh(GameObject prefab, MeshReplacement meshReplacement) { var replacees = prefab.FindAll(meshReplacement.name); foreach (var replacee in replacees) { // one of these two should exist var mf = replacee.GetComponent <MeshFilter>(); if (mf != null) { // if (new Regex("interior").IsMatch(meshReplacement.name)) // { // var renderer = replacee.GetComponent<Renderer>(); // Debug.Log($"{meshReplacement.name} textures are {string.Join(", ", renderer.sharedMaterials.Select(m => $"[{string.Join(", ", new string[] { m?.GetTexture("_t1")?.name, m?.GetTexture("_t3")?.name, m?.GetTexture("_t4")?.name })}]"))}"); // } mf.sharedMesh = meshReplacement.mesh; } var smr = replacee.GetComponent <SkinnedMeshRenderer>(); if (smr != null) { smr.sharedMesh = meshReplacement.mesh; } } return(replacees.Count > 0); }
/// <summary> /// Add interior people flats. /// </summary> private void AddPeople() { GameObject node = new GameObject("People Flats"); node.transform.parent = this.transform; // Add block flats foreach (DFBlock.RmbBlockPeopleRecord obj in recordData.Interior.BlockPeopleRecords) { // Calculate position Vector3 billboardPosition = new Vector3(obj.XPos, -obj.YPos, obj.ZPos) * MeshReader.GlobalScale; // Import 3D character instead of billboard if (MeshReplacement.ImportCustomFlatGameobject(obj.TextureArchive, obj.TextureRecord, billboardPosition, node.transform) != null) { continue; } // Spawn billboard gameobject GameObject go = GameObjectHelper.CreateDaggerfallBillboardGameObject(obj.TextureArchive, obj.TextureRecord, node.transform); // Set position DaggerfallBillboard dfBillboard = go.GetComponent <DaggerfallBillboard>(); go.transform.position = billboardPosition; go.transform.position += new Vector3(0, dfBillboard.Summary.Size.y / 2, 0); // Add RMB data to billboard dfBillboard.SetRMBPeopleData(obj); // Add StaticNPC behaviour StaticNPC npc = go.AddComponent <StaticNPC>(); npc.SetLayoutData(obj); } }
/// <summary> /// Add misc block flats. /// Batching is conditionally supported. /// </summary> public static void AddMiscBlockFlats( ref DFBlock blockData, Transform flatsParent, DaggerfallBillboardBatch animalsBillboardBatch = null, TextureAtlasBuilder miscBillboardsAtlas = null, DaggerfallBillboardBatch miscBillboardsBatch = null) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return; } // Add block flats foreach (DFBlock.RmbBlockFlatObjectRecord obj in blockData.RmbBlock.MiscFlatObjectRecords) { // Ignore lights as they are handled by AddLights() if (obj.TextureArchive == TextureReader.LightsTextureArchive) { continue; } // Calculate position Vector3 billboardPosition = new Vector3( obj.XPos, -obj.YPos + blockFlatsOffsetY, obj.ZPos + BlocksFile.RMBDimension) * MeshReader.GlobalScale; // Import custom 3d gameobject instead of flat if (MeshReplacement.ImportCustomFlatGameobject(obj.TextureArchive, obj.TextureRecord, billboardPosition, flatsParent) != null) { continue; } // Use misc billboard atlas where available if (miscBillboardsAtlas != null && miscBillboardsBatch != null) { TextureAtlasBuilder.AtlasItem item = miscBillboardsAtlas.GetAtlasItem(obj.TextureArchive, obj.TextureRecord); if (item.key != -1) { miscBillboardsBatch.AddItem(item.rect, item.textureItem.size, item.textureItem.scale, billboardPosition); continue; } } // Add to batch where available if (obj.TextureArchive == TextureReader.AnimalsTextureArchive && animalsBillboardBatch != null) { animalsBillboardBatch.AddItem(obj.TextureRecord, billboardPosition); continue; } // Add standalone billboard gameobject GameObject go = GameObjectHelper.CreateDaggerfallBillboardGameObject(obj.TextureArchive, obj.TextureRecord, flatsParent); go.transform.position = billboardPosition; AlignBillboardToBase(go); } }
/// <summary> /// Add interior people flats. /// </summary> private void AddPeople(PlayerGPS.DiscoveredBuilding buildingData) { GameObject node = new GameObject("People Flats"); node.transform.parent = this.transform; bool isMemberOfBuildingGuild = GameManager.Instance.GuildManager.GetGuild(buildingData.factionID).IsMember(); // Add block flats foreach (DFBlock.RmbBlockPeopleRecord obj in recordData.Interior.BlockPeopleRecords) { // Calculate position Vector3 billboardPosition = new Vector3(obj.XPos, -obj.YPos, obj.ZPos) * MeshReader.GlobalScale; // Import 3D character instead of billboard if (MeshReplacement.ImportCustomFlatGameobject(obj.TextureArchive, obj.TextureRecord, billboardPosition, node.transform) != null) { continue; } // Spawn billboard gameobject GameObject go = GameObjectHelper.CreateDaggerfallBillboardGameObject(obj.TextureArchive, obj.TextureRecord, node.transform); // Set position DaggerfallBillboard dfBillboard = go.GetComponent <DaggerfallBillboard>(); go.transform.position = billboardPosition; go.transform.position += new Vector3(0, dfBillboard.Summary.Size.y / 2, 0); // Add RMB data to billboard dfBillboard.SetRMBPeopleData(obj); // Add StaticNPC behaviour StaticNPC npc = go.AddComponent <StaticNPC>(); npc.SetLayoutData(obj, entryDoor.buildingKey); // Disable people if shop or building is closed DFLocation.BuildingTypes buildingType = buildingData.buildingType; if ((RMBLayout.IsShop(buildingType) && !GameManager.Instance.PlayerEnterExit.IsPlayerInsideOpenShop) || (buildingType <= DFLocation.BuildingTypes.Palace && !RMBLayout.IsShop(buildingType) && !PlayerActivate.IsBuildingOpen(buildingType))) { go.SetActive(false); } // Disable people if player owns this house else if (DaggerfallBankManager.IsHouseOwned(buildingData.buildingKey)) { go.SetActive(false); } // Disable people if this is TG/DB house and player is not a member else if (buildingData.buildingType == DFLocation.BuildingTypes.House2 && buildingData.factionID != 0 && !isMemberOfBuildingGuild) { go.SetActive(false); } // Disable people if they are TG spymaster, but not in a legit TG house (TODO: spot any other instances for TG/DB) else if (buildingData.buildingType == DFLocation.BuildingTypes.House2 && buildingData.factionID == 0 && npc.Data.factionID == (int)GuildNpcServices.TG_Spymaster) { go.SetActive(false); } } }
public void RestoreSaveData(object dataIn) { if (!loot) { return; } LootContainerData_v1 data = (LootContainerData_v1)dataIn; if (data.loadID != LoadID) { return; } DaggerfallBillboard billboard = loot.GetComponent <DaggerfallBillboard>(); // Restore position loot.transform.position = data.currentPosition; // Restore appearance if (MeshReplacement.ImportCustomFlatGameobject(data.textureArchive, data.textureRecord, Vector3.zero, loot.transform)) { // Use imported model instead of billboard if (billboard) { Destroy(billboard); } Destroy(GetComponent <MeshRenderer>()); } else if (billboard) { // Restore billboard appearance if present billboard.SetMaterial(data.textureArchive, data.textureRecord); } // Restore items loot.Items.DeserializeItems(data.items); // Restore other data loot.ContainerType = data.containerType; loot.ContainerImage = data.containerImage; loot.LootTableKey = data.lootTableKey; loot.TextureArchive = data.textureArchive; loot.TextureRecord = data.textureRecord; loot.playerOwned = data.playerOwned; loot.customDrop = data.customDrop; loot.name = loot.ContainerType.ToString(); loot.entityName = data.entityName; loot.isEnemyClass = data.isEnemyClass; // Remove loot container if empty if (loot.Items.Count == 0) { GameObjectHelper.RemoveLootContainer(loot); } }
/// <summary> /// Add nature billboards. /// </summary> public static void AddNatureFlats( ref DFBlock blockData, Transform flatsParent, DaggerfallBillboardBatch billboardBatch = null, ClimateNatureSets climateNature = ClimateNatureSets.TemperateWoodland, ClimateSeason climateSeason = ClimateSeason.Summer) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return; } for (int y = 0; y < 16; y++) { for (int x = 0; x < 16; x++) { // Get scenery item - ignore indices -1 (empty) and 0 (marker/waypoint of some kind) DFBlock.RmbGroundScenery scenery = blockData.RmbBlock.FldHeader.GroundData.GroundScenery[x, 15 - y]; if (scenery.TextureRecord < 1) { continue; } // Calculate position Vector3 billboardPosition = new Vector3( x * BlocksFile.TileDimension, natureFlatsOffsetY, y * BlocksFile.TileDimension + BlocksFile.TileDimension) * MeshReader.GlobalScale; // Get Archive int natureArchive = ClimateSwaps.GetNatureArchive(climateNature, climateSeason); // Import custom 3d gameobject instead of flat if (MeshReplacement.ImportCustomFlatGameobject(natureArchive, scenery.TextureRecord, billboardPosition, flatsParent) != null) { continue; } // Add billboard to batch or standalone if (billboardBatch != null) { billboardBatch.AddItem(scenery.TextureRecord, billboardPosition); } else { GameObject go = GameObjectHelper.CreateDaggerfallBillboardGameObject(natureArchive, scenery.TextureRecord, flatsParent); go.transform.position = billboardPosition; AlignBillboardToBase(go); } } } }
/// <summary> /// Adds light flats and prefabs. /// </summary> public static void AddLights( ref DFBlock blockData, Transform flatsParent, Transform lightsParent, DaggerfallBillboardBatch billboardBatch = null) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return; } // Do nothing if import option not enabled or missing prefab if (!dfUnity.Option_ImportLightPrefabs || dfUnity.Option_CityLightPrefab == null) { return; } // Iterate block flats for lights foreach (DFBlock.RmbBlockFlatObjectRecord obj in blockData.RmbBlock.MiscFlatObjectRecords) { // Add point lights if (obj.TextureArchive == TextureReader.LightsTextureArchive) { // Calculate position Vector3 billboardPosition = new Vector3( obj.XPos, -obj.YPos + blockFlatsOffsetY, obj.ZPos + BlocksFile.RMBDimension) * MeshReader.GlobalScale; // Import custom 3d gameobject instead of flat if (MeshReplacement.ImportCustomFlatGameobject(obj.TextureArchive, obj.TextureRecord, billboardPosition, flatsParent) != null) { continue; } // Add billboard to batch or standalone if (billboardBatch != null) { billboardBatch.AddItem(obj.TextureRecord, billboardPosition); } else { GameObject go = GameObjectHelper.CreateDaggerfallBillboardGameObject(obj.TextureArchive, obj.TextureRecord, flatsParent); go.transform.position = billboardPosition; AlignBillboardToBase(go); } // Import light prefab AddLight(dfUnity, obj, lightsParent); } } }
/// <summary> /// Add interior people flats. /// </summary> private void AddPeople(PlayerGPS.DiscoveredBuilding buildingData) { GameObject node = new GameObject(peopleFlats); node.transform.parent = this.transform; IGuild guild = GameManager.Instance.GuildManager.GetGuild(buildingData.factionID); bool isMemberOfBuildingGuild = guild.IsMember(); // Add block flats foreach (DFBlock.RmbBlockPeopleRecord obj in recordData.Interior.BlockPeopleRecords) { // Calculate position Vector3 billboardPosition = new Vector3(obj.XPos, -obj.YPos, obj.ZPos) * MeshReader.GlobalScale; // Make person gameobject GameObject go = MeshReplacement.ImportCustomFlatGameobject(obj.TextureArchive, obj.TextureRecord, billboardPosition, node.transform); if (!go) { // Spawn billboard gameobject go = GameObjectHelper.CreateDaggerfallBillboardGameObject(obj.TextureArchive, obj.TextureRecord, node.transform); // Set position DaggerfallBillboard dfBillboard = go.GetComponent <DaggerfallBillboard>(); go.transform.position = billboardPosition; go.transform.position += new Vector3(0, dfBillboard.Summary.Size.y / 2, 0); // Add RMB data to billboard dfBillboard.SetRMBPeopleData(obj); } // Add StaticNPC behaviour StaticNPC npc = go.AddComponent <StaticNPC>(); npc.SetLayoutData(obj, entryDoor.buildingKey); // Disable people if shop or building is closed DFLocation.BuildingTypes buildingType = buildingData.buildingType; if ((RMBLayout.IsShop(buildingType) && !GameManager.Instance.PlayerEnterExit.IsPlayerInsideOpenShop) || (buildingType <= DFLocation.BuildingTypes.Palace && !RMBLayout.IsShop(buildingType) && !(PlayerActivate.IsBuildingOpen(buildingType) || buildingType == DFLocation.BuildingTypes.GuildHall && guild.HallAccessAnytime()))) { go.SetActive(false); } // Disable people if player owns this house else if (DaggerfallBankManager.IsHouseOwned(buildingData.buildingKey)) { go.SetActive(false); } // Disable people if this is TG/DB house and player is not a member else if (buildingData.buildingType == DFLocation.BuildingTypes.House2 && buildingData.factionID != 0 && !isMemberOfBuildingGuild) { go.SetActive(false); } } }
internal static void Process(GameObject go, bool IsFemale) { if (!IsFemale) { return; } MeshReplacement[] componentsInChildren = go.GetComponentsInChildren <MeshReplacement>(true); for (int i = 0; i < (int)componentsInChildren.Length; i++) { MeshReplacement meshReplacement = componentsInChildren[i]; SkinnedMeshRenderer component = meshReplacement.GetComponent <SkinnedMeshRenderer>(); component.sharedMesh = meshReplacement.Female.sharedMesh; component.rootBone = meshReplacement.Female.rootBone; component.bones = meshReplacement.Female.bones; } }
private static void AddProps( DaggerfallUnity dfUnity, ref DFBlock blockData, out List <StaticDoor> doorsOut, ModelCombiner combiner = null, Transform parent = null) { doorsOut = new List <StaticDoor>(); // Iterate through all misc records foreach (DFBlock.RmbBlock3dObjectRecord obj in blockData.RmbBlock.Misc3dObjectRecords) { // Get model transform Vector3 modelPosition = new Vector3(obj.XPos, -obj.YPos + propsOffsetY, obj.ZPos + BlocksFile.RMBDimension) * MeshReader.GlobalScale; Vector3 modelRotation = new Vector3(-obj.XRotation / BlocksFile.RotationDivisor, -obj.YRotation / BlocksFile.RotationDivisor, -obj.ZRotation / BlocksFile.RotationDivisor); Vector3 modelScale = GetModelScaleVector(obj); Matrix4x4 modelMatrix = Matrix4x4.TRS(modelPosition, Quaternion.Euler(modelRotation), modelScale); // Get model data ModelData modelData; dfUnity.MeshReader.GetModelData(obj.ModelIdNum, out modelData); // Does this model have doors? if (modelData.Doors != null) { doorsOut.AddRange(GameObjectHelper.GetStaticDoors(ref modelData, blockData.Index, 0, modelMatrix)); } // Import custom GameObject if (MeshReplacement.ImportCustomGameobject(obj.ModelIdNum, parent, modelMatrix) != null) { continue; } // Use Daggerfall Model // Add or combine if (combiner == null || IsBulletinBoard(obj.ModelIdNum) || PlayerActivate.HasCustomActivation(obj.ModelIdNum)) { AddStandaloneModel(dfUnity, ref modelData, modelMatrix, parent); } else { combiner.Add(ref modelData, modelMatrix); } } }
private static void PlaceWagon(bool fromSave = false) { if (fromSave == false) { WagonMapPixel = GameManager.Instance.PlayerGPS.CurrentMapPixel; if (!transportManager.HasHorse()) { SetWagonPositionAndRotation(true); DaggerfallUI.MessageBox("You have no horse to pull your wagon."); ItemCollection playerItems = GameManager.Instance.PlayerEntity.Items; for (int i = 0; i < playerItems.Count; i++) { DaggerfallUnityItem item = playerItems.GetItem(i); if (item != null && item.IsOfTemplate(ItemGroups.Transportation, (int)Transportation.Small_cart)) { playerItems.RemoveItem(item); } } } else { SetWagonPositionAndRotation(); } } else { PlaceWagonOnGround(); } Wagon = MeshReplacement.ImportCustomGameobject(wagonModelID, null, WagonMatrix); if (Wagon == null) { Wagon = GameObjectHelper.CreateDaggerfallMeshGameObject(wagonModelID, null); } Wagon.transform.SetPositionAndRotation(WagonPosition, WagonRotation); if (GameManager.Instance.PlayerEnterExit.IsPlayerInsideDungeon) { Wagon.SetActive(false); } else { Wagon.SetActive(true); } WagonDeployed = true; }
private static void DeployTent(bool fromSave = false) { if (fromSave == false) { TentMapPixel = GameManager.Instance.PlayerGPS.CurrentMapPixel; SetTentPositionAndRotation(); } //Attempt to load a model replacement Tent = MeshReplacement.ImportCustomGameobject(tentModelID, null, TentMatrix); if (Tent == null) { Tent = GameObjectHelper.CreateDaggerfallMeshGameObject(tentModelID, null); } //Set the model's position in the world Tent.transform.SetPositionAndRotation(TentPosition, TentRotation); Tent.SetActive(true); TentDeployed = true; }
/// <summary> /// Add interior flats. /// </summary> private void AddFlats() { GameObject node = new GameObject("Interior Flats"); node.transform.parent = this.transform; // Add block flats markers.Clear(); foreach (DFBlock.RmbBlockFlatObjectRecord obj in recordData.Interior.BlockFlatObjectRecords) { // Calculate position Vector3 billboardPosition = new Vector3(obj.XPos, -obj.YPos, obj.ZPos) * MeshReader.GlobalScale; // Import custom 3d gameobject instead of flat if (MeshReplacement.ImportCustomFlatGameobject(obj.TextureArchive, obj.TextureRecord, billboardPosition, node.transform) != null) { continue; } // Spawn billboard gameobject GameObject go = GameObjectHelper.CreateDaggerfallBillboardGameObject(obj.TextureArchive, obj.TextureRecord, node.transform); // Set position DaggerfallBillboard dfBillboard = go.GetComponent <DaggerfallBillboard>(); go.transform.position = billboardPosition; go.transform.position += new Vector3(0, dfBillboard.Summary.Size.y / 2, 0); // Add editor markers to list if (obj.TextureArchive == TextureReader.EditorFlatsTextureArchive) { InteriorEditorMarker marker = new InteriorEditorMarker(); marker.type = (InteriorMarkerTypes)obj.TextureRecord; marker.gameObject = go; markers.Add(marker); } // Add point lights if (obj.TextureArchive == TextureReader.LightsTextureArchive) { AddLight(obj, go.transform); } } }
/// <summary> /// Add interior flats. /// </summary> private void AddFlats() { GameObject node = new GameObject("Interior Flats"); node.transform.parent = this.transform; // Add block flats markers.Clear(); foreach (DFBlock.RmbBlockFlatObjectRecord obj in recordData.Interior.BlockFlatObjectRecords) { // Calculate position Vector3 billboardPosition = new Vector3(obj.XPos, -obj.YPos, obj.ZPos) * MeshReader.GlobalScale; // Import custom 3d gameobject instead of flat if (MeshReplacement.ImportCustomFlatGameobject(obj.TextureArchive, obj.TextureRecord, billboardPosition, node.transform) != null) { continue; } // Spawn billboard gameobject GameObject go = GameObjectHelper.CreateDaggerfallBillboardGameObject(obj.TextureArchive, obj.TextureRecord, node.transform); // Set position DaggerfallBillboard dfBillboard = go.GetComponent <DaggerfallBillboard>(); go.transform.position = billboardPosition; go.transform.position += new Vector3(0, dfBillboard.Summary.Size.y / 2, 0); // Add to enter marker list, which is TEXTURE.199, index 8. // Sometimes marker 199.4 is used where the 199.8 enter marker should be // Being a little forgiving and also accepting 199.4 as enter marker // Will add more of these cases if I find them if (obj.TextureArchive == TextureReader.EditorFlatsTextureArchive && (obj.TextureRecord == 8 || obj.TextureRecord == 4)) { markers.Add(go); } // Add point lights if (obj.TextureArchive == TextureReader.LightsTextureArchive) { AddLight(obj, go.transform); } } }
public static void DeployTent(bool fromSave = false) { if (fromSave == false) { CampMapPixel = GameManager.Instance.PlayerGPS.CurrentMapPixel; SetTentPositionAndRotation(); DaggerfallUI.MessageBox("You set up camp"); } else { PlaceTentOnGround(); } //Attempt to load a model replacement Tent = MeshReplacement.ImportCustomGameobject(tentModelID, null, TentMatrix); Fire = GameObjectHelper.CreateDaggerfallBillboardGameObject(210, 1, null); if (Tent == null) { Tent = GameObjectHelper.CreateDaggerfallMeshGameObject(tentModelID, null); } //Set the model's position in the world Tent.transform.SetPositionAndRotation(TentPosition, TentRotation); if (GameManager.Instance.PlayerEnterExit.IsPlayerInsideDungeon) { FirePosition = Tent.transform.position + (Tent.transform.up * 0.8f); Tent.SetActive(false); } else { FirePosition = Tent.transform.position + (Tent.transform.forward * 3) + (Tent.transform.up * 0.8f); Tent.SetActive(true); } Fire.transform.SetPositionAndRotation(FirePosition, TentRotation); Fire.SetActive(true); AddTorchAudioSource(Fire); GameObject lightsNode = new GameObject("Lights"); lightsNode.transform.parent = Fire.transform; AddLight(DaggerfallUnity.Instance, Fire, lightsNode.transform); CampDeployed = true; FireLit = true; }
public static GameObject AddFlatObject(DFBlock.RdbObject obj) { int archive = obj.Resources.FlatResource.TextureArchive; int record = obj.Resources.FlatResource.TextureRecord; // Add GameObject to scene Vector3 targetPosition = new Vector3(obj.XPos, -obj.YPos, obj.ZPos) * MeshReader.GlobalScale; GameObject go = MeshReplacement.ImportCustomFlatGameobject(archive, record, targetPosition, null, true); if (!go) { // Setup standard billboard and assign RDB data go = GameObjectHelper.CreateDaggerfallBillboardGameObject(archive, record, null); go.transform.position = targetPosition; Billboard dfBillboard = go.GetComponent <Billboard>(); dfBillboard.SetRDBResourceData(obj.Resources.FlatResource); } return(go); }
private void Display3dModelSelection(int selectedIdx) { if (goModel) { Object.Destroy(goModel); goModel = null; } // Position camera and set model id uint modelId = 0; if (housesForSale == null) { camera.transform.position = new Vector3(0, 12, DaggerfallBankManager.GetShipCameraDist((ShipType)selectedIdx)); modelId = DaggerfallBankManager.GetShipModelId((ShipType)selectedIdx); } else { camera.transform.position = new Vector3(0, 3, -20); BuildingSummary house = housesForSale[selectedIdx]; modelId = house.ModelID; } // Inject custom GameObject if available else create standard mesh game object for the model goModel = MeshReplacement.ImportCustomGameobject(modelId, goBankPurchase.transform, new Matrix4x4()); if (goModel == null) { goModel = GameObjectHelper.CreateDaggerfallMeshGameObject(modelId, goBankPurchase.transform); } goModel.layer = layer; // Apply current climate ClimateBases climateBase = ClimateSwaps.FromAPIClimateBase(GameManager.Instance.PlayerGPS.ClimateSettings.ClimateType); ClimateSeason season = (DaggerfallUnity.WorldTime.Now.SeasonValue == DaggerfallDateTime.Seasons.Winter) ? ClimateSeason.Winter : ClimateSeason.Summer; DaggerfallMesh dfMesh = goModel.GetComponent <DaggerfallMesh>(); dfMesh.SetClimate(climateBase, season, WindowStyle.Day); }
public static GameObject Add3dObject(DFBlock.RdbObject obj, ref DFBlock.RdbModelReference[] modelReferenceList) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; // Get model reference index and id int modelReference = obj.Resources.ModelResource.ModelIndex; uint modelId = modelReferenceList[modelReference].ModelIdNum; // Get matrix Vector3 modelRotation = new Vector3(-obj.Resources.ModelResource.XRotation / BlocksFile.RotationDivisor, -obj.Resources.ModelResource.YRotation / BlocksFile.RotationDivisor, -obj.Resources.ModelResource.ZRotation / BlocksFile.RotationDivisor); Vector3 modelPosition = new Vector3(obj.XPos, -obj.YPos, obj.ZPos) * MeshReader.GlobalScale; Matrix4x4 modelMatrix = Matrix4x4.TRS(modelPosition, Quaternion.Euler(modelRotation), Vector3.one); // Get model data ModelData modelData; dfUnity.MeshReader.GetModelData(modelId, out modelData); // Get GameObject GameObject modelGO = MeshReplacement.ImportCustomGameobject(modelId, null, modelMatrix, false); if (modelGO == null) { if (modelData.DFMesh.TotalVertices != 0) { modelGO = GameObjectHelper.CreateDaggerfallMeshGameObject(modelId, null); modelGO.transform.position = modelMatrix.GetColumn(3); modelGO.transform.rotation = modelMatrix.rotation; modelGO.transform.localScale = modelMatrix.lossyScale; } else { Debug.LogError("Custom model not found for modelId " + modelId); } } return(modelGO); }
/// <summary> /// Add action doors to parent transform. /// </summary> private void AddActionDoors() { // Using 9000-9005 here but identical door models are also found at 900x, 910x, through to 980x // They seem to be duplicate models but can have different model origins so not all ranges are suitable const int doorModelBaseId = 9000; GameObject actionDoorsNode = new GameObject("Action Doors"); actionDoorsNode.transform.parent = this.transform; foreach (DFBlock.RmbBlockDoorRecord obj in recordData.Interior.BlockDoorRecords) { // Create unique LoadID for save sytem ulong loadID = (ulong)(blockData.Position + obj.Position); // Get model transform Vector3 modelRotation = new Vector3(0, -obj.YRotation / BlocksFile.RotationDivisor, 0); Vector3 modelPosition = new Vector3(obj.XPos, -obj.YPos, obj.ZPos) * MeshReader.GlobalScale; // Instantiate door prefab and add model - DoorModelIndex is modulo to known-good range just in case // A custom prefab can be provided by mods and must include DaggerfallActionDoor component with all requirements. uint modelId = (uint)(doorModelBaseId + obj.DoorModelIndex % 5); GameObject go = MeshReplacement.ImportCustomGameobject(modelId, actionDoorsNode.transform, Matrix4x4.identity); if (!go) { go = GameObjectHelper.InstantiatePrefab(dfUnity.Option_InteriorDoorPrefab.gameObject, string.Empty, actionDoorsNode.transform, Vector3.zero); GameObjectHelper.CreateDaggerfallMeshGameObject(modelId, actionDoorsNode.transform, false, go, true); // Resize box collider to new mesh bounds BoxCollider boxCollider = go.GetComponent <BoxCollider>(); MeshRenderer meshRenderer = go.GetComponent <MeshRenderer>(); if (boxCollider != null && meshRenderer != null) { boxCollider.center = meshRenderer.bounds.center; boxCollider.size = meshRenderer.bounds.size; } // Update climate DaggerfallMesh dfMesh = go.GetComponent <DaggerfallMesh>(); dfMesh.SetClimate(climateBase, climateSeason, WindowStyle.Disabled); } // Apply transforms go.transform.rotation = Quaternion.Euler(modelRotation); go.transform.position = modelPosition; // Get action door script DaggerfallActionDoor actionDoor = go.GetComponent <DaggerfallActionDoor>(); // Assign loadID if (actionDoor) { actionDoor.LoadID = loadID; } if (SaveLoadManager.Instance != null) { go.AddComponent <SerializableActionDoor>(); } } }
private static void AddModels( DaggerfallUnity dfUnity, int layoutX, int layoutY, ref DFBlock blockData, out List <StaticDoor> doorsOut, out List <StaticBuilding> buildingsOut, ModelCombiner combiner = null, Transform parent = null) { doorsOut = new List <StaticDoor>(); buildingsOut = new List <StaticBuilding>(); // Iterate through all subrecords int recordCount = 0; foreach (DFBlock.RmbSubRecord subRecord in blockData.RmbBlock.SubRecords) { // Get subrecord transform Vector3 subRecordPosition = new Vector3(subRecord.XPos, 0, BlocksFile.RMBDimension - subRecord.ZPos) * MeshReader.GlobalScale; Vector3 subRecordRotation = new Vector3(0, -subRecord.YRotation / BlocksFile.RotationDivisor, 0); Matrix4x4 subRecordMatrix = Matrix4x4.TRS(subRecordPosition, Quaternion.Euler(subRecordRotation), Vector3.one); // Iterate through models in this subrecord bool firstModel = true; foreach (DFBlock.RmbBlock3dObjectRecord obj in subRecord.Exterior.Block3dObjectRecords) { // Get model transform Vector3 modelPosition = new Vector3(obj.XPos, -obj.YPos, obj.ZPos) * MeshReader.GlobalScale; Vector3 modelRotation = new Vector3(-obj.XRotation / BlocksFile.RotationDivisor, -obj.YRotation / BlocksFile.RotationDivisor, -obj.ZRotation / BlocksFile.RotationDivisor); Vector3 modelScale = GetModelScaleVector(obj); Matrix4x4 modelMatrix = subRecordMatrix * Matrix4x4.TRS(modelPosition, Quaternion.Euler(modelRotation), modelScale); // Get model data ModelData modelData; dfUnity.MeshReader.GetModelData(obj.ModelIdNum, out modelData); // Does this model have doors? StaticDoor[] staticDoors = null; if (modelData.Doors != null) { staticDoors = GameObjectHelper.GetStaticDoors(ref modelData, blockData.Index, recordCount, modelMatrix); } // Store building information for first model of record // First model is main record structure, others are attachments like posts // Only main structure is needed to resolve building after hit-test int buildingKey = 0; if (firstModel) { // Create building key for this record - considered experimental for now buildingKey = BuildingDirectory.MakeBuildingKey((byte)layoutX, (byte)layoutY, (byte)recordCount); StaticBuilding staticBuilding = new StaticBuilding(); staticBuilding.modelMatrix = modelMatrix; staticBuilding.recordIndex = recordCount; staticBuilding.centre = new Vector3(modelData.DFMesh.Centre.X, modelData.DFMesh.Centre.Y, modelData.DFMesh.Centre.Z) * MeshReader.GlobalScale; staticBuilding.size = new Vector3(modelData.DFMesh.Size.X, modelData.DFMesh.Size.Y, modelData.DFMesh.Size.Z) * MeshReader.GlobalScale; buildingsOut.Add(staticBuilding); firstModel = false; } bool dontCreateStaticDoors = false; // Import custom GameObject or use Daggerfall Model GameObject go; if (go = MeshReplacement.ImportCustomGameobject(obj.ModelIdNum, parent, modelMatrix)) { // Find doors if (staticDoors != null && staticDoors.Length > 0) { CustomDoor.InitDoors(go, staticDoors, buildingKey, out dontCreateStaticDoors); } } else if (combiner == null || IsCityGate(obj.ModelIdNum) || IsBulletinBoard(obj.ModelIdNum) || PlayerActivate.HasCustomActivation(obj.ModelIdNum)) { AddStandaloneModel(dfUnity, ref modelData, modelMatrix, parent); } else { combiner.Add(ref modelData, modelMatrix); } if (modelData.Doors != null && !dontCreateStaticDoors) { doorsOut.AddRange(staticDoors); } } // Increment record count recordCount++; } }
/// <summary> /// Add interior models. /// </summary> private void AddModels() { List <StaticDoor> doors = new List <StaticDoor>(); GameObject node = new GameObject("Models"); GameObject doorsNode = new GameObject("Doors"); node.transform.parent = this.transform; doorsNode.transform.parent = this.transform; // Iterate through models in this subrecord combiner.NewCombiner(); foreach (DFBlock.RmbBlock3dObjectRecord obj in recordData.Interior.Block3dObjectRecords) { // Get model data ModelData modelData; dfUnity.MeshReader.GetModelData(obj.ModelIdNum, out modelData); // Get model position by type (3 seems to indicate props/clutter) Vector3 modelPosition; if (obj.ObjectType == 3) { // Props axis needs to be transformed to lowest Y point Vector3 bottom = modelData.Vertices[0]; for (int i = 0; i < modelData.Vertices.Length; i++) { if (modelData.Vertices[i].y < bottom.y) { bottom = modelData.Vertices[i]; } } modelPosition = new Vector3(obj.XPos, obj.YPos, obj.ZPos) * MeshReader.GlobalScale; modelPosition += new Vector3(0, -bottom.y, 0); } else { modelPosition = new Vector3(obj.XPos, -obj.YPos, obj.ZPos) * MeshReader.GlobalScale; } // Get model transform Vector3 modelRotation = new Vector3(0, -obj.YRotation / BlocksFile.RotationDivisor, 0); Matrix4x4 modelMatrix = Matrix4x4.TRS(modelPosition, Quaternion.Euler(modelRotation), Vector3.one); // Does this model have doors? if (modelData.Doors != null) { doors.AddRange(GameObjectHelper.GetStaticDoors(ref modelData, entryDoor.blockIndex, entryDoor.recordIndex, modelMatrix)); } // Get GameObject if (MeshReplacement.ImportCustomGameobject(obj.ModelIdNum, node.transform, modelMatrix) == null) { // Use Daggerfall Mesh: Combine or add if (dfUnity.Option_CombineRMB) { combiner.Add(ref modelData, modelMatrix); } else { // Add GameObject GameObject go = GameObjectHelper.CreateDaggerfallMeshGameObject(obj.ModelIdNum, node.transform, dfUnity.Option_SetStaticFlags); go.transform.position = modelMatrix.GetColumn(3); go.transform.rotation = GameObjectHelper.QuaternionFromMatrix(modelMatrix); // Update climate DaggerfallMesh dfMesh = go.GetComponent <DaggerfallMesh>(); dfMesh.SetClimate(climateBase, climateSeason, WindowStyle.Disabled); } } } // Add combined GameObject if (dfUnity.Option_CombineRMB) { if (combiner.VertexCount > 0) { combiner.Apply(); GameObject go = GameObjectHelper.CreateCombinedMeshGameObject(combiner, "CombinedModels", node.transform, dfUnity.Option_SetStaticFlags); // Update climate DaggerfallMesh dfMesh = go.GetComponent <DaggerfallMesh>(); dfMesh.SetClimate(climateBase, climateSeason, WindowStyle.Disabled); } } // Add static doors component DaggerfallStaticDoors c = this.gameObject.AddComponent <DaggerfallStaticDoors>(); c.Doors = doors.ToArray(); }
/// <summary> /// Add misc block flats. /// Batching is conditionally supported. /// </summary> public static void AddMiscBlockFlats( ref DFBlock blockData, Transform flatsParent, DaggerfallBillboardBatch animalsBillboardBatch = null, TextureAtlasBuilder miscBillboardsAtlas = null, DaggerfallBillboardBatch miscBillboardsBatch = null) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return; } // Add block flats foreach (DFBlock.RmbBlockFlatObjectRecord obj in blockData.RmbBlock.MiscFlatObjectRecords) { // Ignore lights as they are handled by AddLights() if (obj.TextureArchive == TextureReader.LightsTextureArchive) { continue; } // Calculate position Vector3 billboardPosition = new Vector3( obj.XPos, -obj.YPos + blockFlatsOffsetY, obj.ZPos + BlocksFile.RMBDimension) * MeshReader.GlobalScale; // Import custom 3d gameobject instead of flat if (MeshReplacement.ImportCustomFlatGameobject(obj.TextureArchive, obj.TextureRecord, billboardPosition, flatsParent) != null) { continue; } //// Use misc billboard atlas where available //if (miscBillboardsAtlas != null && miscBillboardsBatch != null) //{ // TextureAtlasBuilder.AtlasItem item = miscBillboardsAtlas.GetAtlasItem(obj.TextureArchive, obj.TextureRecord); // if (item.key != -1) // { // miscBillboardsBatch.AddItem(item.rect, item.textureItem.size, item.textureItem.scale, billboardPosition); // continue; // } //} // Add to batch where available //if (obj.TextureArchive == TextureReader.AnimalsTextureArchive && animalsBillboardBatch != null) //{ // animalsBillboardBatch.AddItem(obj.TextureRecord, billboardPosition); // continue; //} // Add standalone billboard gameobject GameObject go = GameObjectHelper.CreateDaggerfallBillboardGameObject(obj.TextureArchive, obj.TextureRecord, flatsParent); go.transform.position = billboardPosition; AlignBillboardToBase(go); // Add animal sound if (obj.TextureArchive == TextureReader.AnimalsTextureArchive) { AddAnimalAudioSource(go); } // If flat record has a non-zero faction id, then it's an exterior NPC if (obj.FactionID != 0) { // Add RMB data to billboard DaggerfallBillboard dfBillboard = go.GetComponent <DaggerfallBillboard>(); dfBillboard.SetRMBPeopleData(obj.FactionID, obj.Flags, obj.Position); // Add StaticNPC behaviour StaticNPC npc = go.AddComponent <StaticNPC>(); npc.SetLayoutData(obj); } } }
/// <summary> /// Add misc block flats. /// Batching is conditionally supported. /// </summary> public static void AddMiscBlockFlats( ref DFBlock blockData, Transform flatsParent, int mapId, int locationIndex, DaggerfallBillboardBatch animalsBillboardBatch = null, TextureAtlasBuilder miscBillboardsAtlas = null, DaggerfallBillboardBatch miscBillboardsBatch = null) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return; } // Add block flats foreach (DFBlock.RmbBlockFlatObjectRecord obj in blockData.RmbBlock.MiscFlatObjectRecords) { // Ignore lights as they are handled by AddLights() if (obj.TextureArchive == TextureReader.LightsTextureArchive) { continue; } // Calculate position Vector3 billboardPosition = new Vector3( obj.XPos, -obj.YPos + blockFlatsOffsetY, obj.ZPos + BlocksFile.RMBDimension) * MeshReader.GlobalScale; GameObject go = MeshReplacement.ImportCustomFlatGameobject(obj.TextureArchive, obj.TextureRecord, billboardPosition, flatsParent); if (go == null) { // Add standalone billboard gameobject go = GameObjectHelper.CreateDaggerfallBillboardGameObject(obj.TextureArchive, obj.TextureRecord, flatsParent); go.transform.position = billboardPosition; AlignBillboardToBase(go); } // Add animal sound if (obj.TextureArchive == TextureReader.AnimalsTextureArchive) { AddAnimalAudioSource(go, obj.TextureRecord); } // If flat record has a non-zero faction id, then it's an exterior NPC if (obj.FactionID != 0) { // Add RMB data to billboard Billboard dfBillboard = go.GetComponent <Billboard>(); if (dfBillboard != null) { dfBillboard.SetRMBPeopleData(obj.FactionID, obj.Flags, obj.Position); } // Add StaticNPC behaviour StaticNPC npc = go.AddComponent <StaticNPC>(); npc.SetLayoutData(obj, mapId, locationIndex); } } }
// Drops nature flats based on random chance scaled by simple rules public static void LayoutNatureBillboards(DaggerfallTerrain dfTerrain, DaggerfallBillboardBatch dfBillboardBatch, float terrainScale, int terrainDist) { const float maxSteepness = 50f; // 50 const float slopeSinkRatio = 70f; // Sink flats slightly into ground as slope increases to prevent floaty trees. const float baseChanceOnDirt = 0.2f; // 0.2 const float baseChanceOnGrass = 0.9f; // 0.4 const float baseChanceOnStone = 0.05f; // 0.05 // Location Rect is expanded slightly to give extra clearance around locations const int natureClearance = 4; Rect rect = dfTerrain.MapData.locationRect; if (rect.x > 0 && rect.y > 0) { rect.xMin -= natureClearance; rect.xMax += natureClearance; rect.yMin -= natureClearance; rect.yMax += natureClearance; } // Chance scaled based on map pixel height // This tends to produce sparser lowlands and denser highlands // Adjust or remove clamp range to influence nature generation float elevationScale = (dfTerrain.MapData.worldHeight / 128f); elevationScale = Mathf.Clamp(elevationScale, 0.4f, 1.0f); // Chance scaled by base climate type float climateScale = 1.0f; DFLocation.ClimateSettings climate = MapsFile.GetWorldClimateSettings(dfTerrain.MapData.worldClimate); switch (climate.ClimateType) { case DFLocation.ClimateBaseType.Desert: // Just lower desert for now climateScale = 0.25f; break; } float chanceOnDirt = baseChanceOnDirt * elevationScale * climateScale; float chanceOnGrass = baseChanceOnGrass * elevationScale * climateScale; float chanceOnStone = baseChanceOnStone * elevationScale * climateScale; // Get terrain Terrain terrain = dfTerrain.gameObject.GetComponent <Terrain>(); if (!terrain) { return; } // Get terrain data TerrainData terrainData = terrain.terrainData; if (!terrainData) { return; } // Remove exiting billboards dfBillboardBatch.Clear(); MeshReplacement.ClearNatureGameObjects(terrain); // Seed random with terrain key Random.InitState(MakeTerrainKey(dfTerrain.MapPixelX, dfTerrain.MapPixelY)); // Just layout some random flats spread evenly across entire map pixel area // Flats are aligned with tiles, max 16129 billboards per batch Vector2 tilePos = Vector2.zero; int tDim = MapsFile.WorldMapTileDim; int hDim = DaggerfallUnity.Instance.TerrainSampler.HeightmapDimension; float scale = terrainData.heightmapScale.x * (float)hDim / (float)tDim; float maxTerrainHeight = DaggerfallUnity.Instance.TerrainSampler.MaxTerrainHeight; float beachLine = DaggerfallUnity.Instance.TerrainSampler.BeachElevation; for (int y = 0; y < tDim; y++) { for (int x = 0; x < tDim; x++) { // Reject based on steepness float steepness = terrainData.GetSteepness((float)x / tDim, (float)y / tDim); if (steepness > maxSteepness) { continue; } // Reject if inside location rect (expanded slightly to give extra clearance around locations) tilePos.x = x; tilePos.y = y; if (rect.x > 0 && rect.y > 0 && rect.Contains(tilePos)) { continue; } // Chance also determined by tile type int tile = dfTerrain.MapData.tilemapSamples[x, y] & 0x3F; if (tile == 1) { // Dirt if (Random.Range(0f, 1f) > chanceOnDirt) { continue; } } else if (tile == 2) { // Grass if (Random.Range(0f, 1f) > chanceOnGrass) { continue; } } else if (tile == 3) { // Stone if (Random.Range(0f, 1f) > chanceOnStone) { continue; } } else { // Anything else continue; } int hx = (int)Mathf.Clamp(hDim * ((float)x / (float)tDim), 0, hDim - 1); int hy = (int)Mathf.Clamp(hDim * ((float)y / (float)tDim), 0, hDim - 1); float height = dfTerrain.MapData.heightmapSamples[hy, hx] * maxTerrainHeight; // x & y swapped in heightmap for TerrainData.SetHeights() // Reject if too close to water if (height < beachLine) { continue; } // Sample height and position billboard Vector3 pos = new Vector3(x * scale, 0, y * scale); float height2 = terrain.SampleHeight(pos + terrain.transform.position); pos.y = height2 - (steepness / slopeSinkRatio); // Add to batch unless a mesh replacement is found int record = Random.Range(1, 32); if (terrainDist > 1 || !MeshReplacement.ImportNatureGameObject(dfBillboardBatch.TextureArchive, record, terrain, x, y)) { dfBillboardBatch.AddItem(record, pos); } else if (!NatureMeshUsed) { NatureMeshUsed = true; // Signal that nature mesh has been used to initiate extra terrain updates } } } // Apply new batch dfBillboardBatch.Apply(); }
/// <summary> /// Add subrecord (building) exterior block flats. /// </summary> public static void AddExteriorBlockFlats( ref DFBlock blockData, Transform flatsParent, Transform lightsParent, int mapId, int locationIndex, ClimateNatureSets climateNature = ClimateNatureSets.TemperateWoodland, ClimateSeason climateSeason = ClimateSeason.Summer) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return; } // Get Nature Archive int natureArchive = ClimateSwaps.GetNatureArchive(climateNature, climateSeason); foreach (DFBlock.RmbSubRecord subRecord in blockData.RmbBlock.SubRecords) { Vector3 subRecordPosition = new Vector3(subRecord.XPos, 0, -subRecord.ZPos) * MeshReader.GlobalScale; foreach (DFBlock.RmbBlockFlatObjectRecord obj in subRecord.Exterior.BlockFlatObjectRecords) { // Don't add building exterior editor flats since they can't be used by any DFU systems int archive = obj.TextureArchive; if (archive == TextureReader.EditorFlatsTextureArchive) { continue; } // Calculate position Vector3 billboardPosition = new Vector3( obj.XPos, -obj.YPos + blockFlatsOffsetY, obj.ZPos + BlocksFile.RMBDimension) * MeshReader.GlobalScale; billboardPosition += subRecordPosition; // Add natures using correct climate set archive if (archive >= (int)DFLocation.ClimateTextureSet.Nature_RainForest && archive <= (int)DFLocation.ClimateTextureSet.Nature_Mountains_Snow) { archive = natureArchive; billboardPosition.z = natureFlatsOffsetY; } GameObject go = MeshReplacement.ImportCustomFlatGameobject(archive, obj.TextureRecord, billboardPosition, flatsParent); bool isImported = go != null; if (!isImported) { // Add standalone billboard gameobject go = GameObjectHelper.CreateDaggerfallBillboardGameObject(archive, obj.TextureRecord, flatsParent); go.transform.position = billboardPosition; AlignBillboardToBase(go); } // Add animal sound if (archive == TextureReader.AnimalsTextureArchive) { AddAnimalAudioSource(go, obj.TextureRecord); } // If flat record has a non-zero faction id, then it's an exterior NPC if (obj.FactionID != 0) { // Add RMB data to billboard Billboard dfBillboard = go.GetComponent <Billboard>(); if (dfBillboard != null) { dfBillboard.SetRMBPeopleData(obj.FactionID, obj.Flags, obj.Position); } // Add StaticNPC behaviour StaticNPC npc = go.AddComponent <StaticNPC>(); npc.SetLayoutData(obj, mapId, locationIndex); } // If this is a light flat, import light prefab if (archive == TextureReader.LightsTextureArchive && !isImported) { if (dfUnity.Option_CityLightPrefab == null) { return; } Vector2 size = dfUnity.MeshReader.GetScaledBillboardSize(210, obj.TextureRecord); Vector3 position = new Vector3( obj.XPos, -obj.YPos + size.y, obj.ZPos + BlocksFile.RMBDimension) * MeshReader.GlobalScale; position += subRecordPosition; GameObjectHelper.InstantiatePrefab(dfUnity.Option_CityLightPrefab.gameObject, string.Empty, lightsParent, position); } } } }
/// <summary> /// Add interior models. /// </summary> private void AddModels(PlayerGPS.DiscoveredBuilding buildingData) { List <StaticDoor> doors = new List <StaticDoor>(); GameObject node = new GameObject("Models"); GameObject doorsNode = new GameObject("Doors"); node.transform.parent = this.transform; doorsNode.transform.parent = this.transform; // Iterate through models in this subrecord combiner.NewCombiner(); foreach (DFBlock.RmbBlock3dObjectRecord obj in recordData.Interior.Block3dObjectRecords) { bool stopCombine = false; // Get model data ModelData modelData; dfUnity.MeshReader.GetModelData(obj.ModelIdNum, out modelData); // Get model position by type (3 seems to indicate props/clutter) // Also stop these from being combined as some may carry a loot container Vector3 modelPosition; if (obj.ObjectType == propModelType) { // Props axis needs to be transformed to lowest Y point Vector3 bottom = modelData.Vertices[0]; for (int i = 0; i < modelData.Vertices.Length; i++) { if (modelData.Vertices[i].y < bottom.y) { bottom = modelData.Vertices[i]; } } modelPosition = new Vector3(obj.XPos, obj.YPos, obj.ZPos) * MeshReader.GlobalScale; modelPosition += new Vector3(0, -bottom.y, 0); stopCombine = true; } else { modelPosition = new Vector3(obj.XPos, -obj.YPos, obj.ZPos) * MeshReader.GlobalScale; } // Stop special object from being combined if (obj.ModelIdNum == ladderModelId) { stopCombine = true; } // Get model transform Vector3 modelRotation = new Vector3(0, -obj.YRotation / BlocksFile.RotationDivisor, 0); Matrix4x4 modelMatrix = Matrix4x4.TRS(modelPosition, Quaternion.Euler(modelRotation), Vector3.one); // Does this model have doors? if (modelData.Doors != null) { doors.AddRange(GameObjectHelper.GetStaticDoors(ref modelData, entryDoor.blockIndex, entryDoor.recordIndex, modelMatrix)); } // Inject custom GameObject if available GameObject go = MeshReplacement.ImportCustomGameobject(obj.ModelIdNum, node.transform, modelMatrix); // Otherwise use Daggerfall mesh - combine or add if (!go) { if (dfUnity.Option_CombineRMB && !stopCombine) { combiner.Add(ref modelData, modelMatrix); } else { // Add individual GameObject go = GameObjectHelper.CreateDaggerfallMeshGameObject(obj.ModelIdNum, node.transform, dfUnity.Option_SetStaticFlags); go.transform.position = modelMatrix.GetColumn(3); go.transform.rotation = GameObjectHelper.QuaternionFromMatrix(modelMatrix); // Update climate DaggerfallMesh dfMesh = go.GetComponent <DaggerfallMesh>(); dfMesh.SetClimate(climateBase, climateSeason, WindowStyle.Disabled); } } // Make ladder collider convex if (obj.ModelIdNum == ladderModelId) { var meshCollider = go.GetComponent <MeshCollider>(); if (meshCollider) { meshCollider.convex = true; } go.AddComponent <DaggerfallLadder>(); } // Optionally add action objects to specific furniture items (e.g. loot containers), except when laying out map (buildingType=AllValid) if (obj.ObjectType == propModelType && buildingData.buildingType != DFLocation.BuildingTypes.AllValid) { AddFurnitureAction(obj, go, buildingData); } } // Add combined GameObject if (dfUnity.Option_CombineRMB) { if (combiner.VertexCount > 0) { combiner.Apply(); GameObject go = GameObjectHelper.CreateCombinedMeshGameObject(combiner, "CombinedModels", node.transform, dfUnity.Option_SetStaticFlags); // Update climate DaggerfallMesh dfMesh = go.GetComponent <DaggerfallMesh>(); dfMesh.SetClimate(climateBase, climateSeason, WindowStyle.Disabled); } } // Add static doors component DaggerfallStaticDoors c = this.gameObject.AddComponent <DaggerfallStaticDoors>(); c.Doors = doors.ToArray(); }
/// <summary> /// Add interior flats. /// </summary> private void AddFlats(PlayerGPS.DiscoveredBuilding buildingData) { GameObject node = new GameObject("Interior Flats"); node.transform.parent = this.transform; // Add block flats markers.Clear(); foreach (DFBlock.RmbBlockFlatObjectRecord obj in recordData.Interior.BlockFlatObjectRecords) { // Calculate position Vector3 billboardPosition = new Vector3(obj.XPos, -obj.YPos, obj.ZPos) * MeshReader.GlobalScale; // Import custom 3d gameobject instead of flat if (MeshReplacement.ImportCustomFlatGameobject(obj.TextureArchive, obj.TextureRecord, billboardPosition, node.transform) != null) { continue; } // Spawn billboard gameobject GameObject go = GameObjectHelper.CreateDaggerfallBillboardGameObject(obj.TextureArchive, obj.TextureRecord, node.transform); // Set position DaggerfallBillboard dfBillboard = go.GetComponent <DaggerfallBillboard>(); go.transform.position = billboardPosition; go.transform.position += new Vector3(0, dfBillboard.Summary.Size.y / 2, 0); // Add editor markers to list if (obj.TextureArchive == TextureReader.EditorFlatsTextureArchive) { InteriorEditorMarker marker = new InteriorEditorMarker(); marker.type = (InteriorMarkerTypes)obj.TextureRecord; marker.gameObject = go; markers.Add(marker); // Add loot containers for treasure markers (always use pile of clothes icon) if (marker.type == InteriorMarkerTypes.Treasure) { // Create unique LoadID for save system, using 9 lsb and the sign bit from each coord pos int ulong loadID = ((ulong)buildingData.buildingKey) << 30 | (uint)(obj.XPos << 1 & posMask) << 20 | (uint)(obj.YPos << 1 & posMask) << 10 | (uint)(obj.ZPos << 1 & posMask); DaggerfallLoot loot = GameObjectHelper.CreateLootContainer( LootContainerTypes.RandomTreasure, InventoryContainerImages.Chest, billboardPosition, node.transform, DaggerfallLootDataTables.clothingArchive, 0, loadID); if (!LootTables.GenerateLoot(loot, (int)GameManager.Instance.PlayerGPS.CurrentLocationType)) { DaggerfallUnity.LogMessage(string.Format("DaggerfallInterior: Location type {0} is out of range or unknown.", GameManager.Instance.PlayerGPS.CurrentLocationType), true); } } } // Add point lights if (obj.TextureArchive == TextureReader.LightsTextureArchive) { AddLight(obj, go.transform); } } }
private static void AddModels( DaggerfallUnity dfUnity, ref DFBlock blockData, out List <StaticDoor> doorsOut, out List <StaticBuilding> buildingsOut, ModelCombiner combiner = null, Transform parent = null) { doorsOut = new List <StaticDoor>(); buildingsOut = new List <StaticBuilding>(); // Iterate through all subrecords int recordCount = 0; foreach (DFBlock.RmbSubRecord subRecord in blockData.RmbBlock.SubRecords) { // Get subrecord transform Vector3 subRecordPosition = new Vector3(subRecord.XPos, 0, BlocksFile.RMBDimension - subRecord.ZPos) * MeshReader.GlobalScale; Vector3 subRecordRotation = new Vector3(0, -subRecord.YRotation / BlocksFile.RotationDivisor, 0); Matrix4x4 subRecordMatrix = Matrix4x4.TRS(subRecordPosition, Quaternion.Euler(subRecordRotation), Vector3.one); // Iterate through models in this subrecord bool firstModel = true; foreach (DFBlock.RmbBlock3dObjectRecord obj in subRecord.Exterior.Block3dObjectRecords) { // Get model transform Vector3 modelPosition = new Vector3(obj.XPos, -obj.YPos, obj.ZPos) * MeshReader.GlobalScale; Vector3 modelRotation = new Vector3(0, -obj.YRotation / BlocksFile.RotationDivisor, 0); Matrix4x4 modelMatrix = subRecordMatrix * Matrix4x4.TRS(modelPosition, Quaternion.Euler(modelRotation), Vector3.one); // Get model data ModelData modelData; dfUnity.MeshReader.GetModelData(obj.ModelIdNum, out modelData); // Does this model have doors? if (modelData.Doors != null) { doorsOut.AddRange(GameObjectHelper.GetStaticDoors(ref modelData, blockData.Index, recordCount, modelMatrix)); } // Store building information for first model of record // First model is main record structure, others are attachments like posts // Only main structure is needed to resolve building after hit-test if (firstModel) { StaticBuilding staticBuilding = new StaticBuilding(); staticBuilding.modelMatrix = modelMatrix; staticBuilding.recordIndex = recordCount; staticBuilding.centre = new Vector3(modelData.DFMesh.Centre.X, modelData.DFMesh.Centre.Y, modelData.DFMesh.Centre.Z) * MeshReader.GlobalScale; staticBuilding.size = new Vector3(modelData.DFMesh.Size.X, modelData.DFMesh.Size.Y, modelData.DFMesh.Size.Z) * MeshReader.GlobalScale; buildingsOut.Add(staticBuilding); firstModel = false; } // Import custom GameObject if (MeshReplacement.ImportCustomGameobject(obj.ModelIdNum, parent, modelMatrix) != null) { continue; } // Use Daggerfall Model // Add or combine if (combiner == null || IsCityGate(obj.ModelIdNum)) { AddStandaloneModel(dfUnity, ref modelData, modelMatrix, parent); } else { combiner.Add(ref modelData, modelMatrix); } } // Increment record count recordCount++; } }
public static GameObject CreatePlacedObject(PlacedObjectData_v2 data, Transform parent, bool previewGo = false) { // Custom models like Handpainted Models have insanley different scales (< 0.0 to 200+) Set all models as a child to a parent, so // EditMode can uniformly scale properly. GameObject parentGo = new GameObject(); GameObject childGo; parentGo.transform.parent = parent; if (data.modelID == 0) { childGo = MeshReplacement.ImportCustomFlatGameobject(data.archive, data.record, Vector3.zero, parentGo.transform); if (childGo == null) { childGo = GameObjectHelper.CreateDaggerfallBillboardGameObject(data.archive, data.record, parentGo.transform); } } else { Matrix4x4 matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, Vector3.one); childGo = MeshReplacement.ImportCustomGameobject(data.modelID, parentGo.transform, matrix); if (childGo == null) { childGo = GameObjectHelper.CreateDaggerfallMeshGameObject(data.modelID, parentGo.transform); } } parentGo.transform.eulerAngles = Vector3.zero; childGo.transform.eulerAngles = Vector3.zero; if (previewGo) { data.isLight = true; } BoxCollider parentCollider = parentGo.AddComponent <BoxCollider>(); BoxCollider childCollider; //Expanding collider a little gives better hit detection. float buffer = 0.02f; // Some custom models have a box collider and are made of multiple smaller models. Get the parent collider size. if (childCollider = childGo.GetComponent <BoxCollider>()) { parentCollider.size = new Vector3((childCollider.size.x * childGo.transform.localScale.x) + buffer, (childCollider.size.y * childGo.transform.localScale.y) + buffer, (childCollider.size.z * childGo.transform.localScale.z) + buffer); parentCollider.center = new Vector3(childCollider.center.x * childGo.transform.localScale.x, childCollider.center.y * childGo.transform.localScale.y, childCollider.center.z * childGo.transform.localScale.z); // Child colliders screw with EditMode. GameObject.Destroy(childCollider); } else { Bounds childBounds = childGo.GetComponent <MeshFilter>().sharedMesh.bounds; parentCollider.size = new Vector3((childBounds.size.x * childGo.transform.localScale.x) + buffer, (childBounds.size.y * childGo.transform.localScale.y) + buffer, (childBounds.size.z * childGo.transform.localScale.z) + buffer); parentCollider.center = new Vector3(childBounds.center.x * childGo.transform.localScale.x, childBounds.center.y * childGo.transform.localScale.y, childBounds.center.z * childGo.transform.localScale.z); } parentCollider.isTrigger = true; parentGo.AddComponent <PlacedObject>(); SetPlacedObject(data, parentGo); return(parentGo); }
public void RestoreSaveData(object dataIn) { if (!loot) { return; } LootContainerData_v1 data = (LootContainerData_v1)dataIn; if (data.loadID != LoadID) { return; } // Restore billboard only if this is a billboard-based loot container if (loot.ContainerType == LootContainerTypes.RandomTreasure || loot.ContainerType == LootContainerTypes.CorpseMarker || loot.ContainerType == LootContainerTypes.DroppedLoot) { DaggerfallBillboard billboard = loot.GetComponent <DaggerfallBillboard>(); // Interiors and exteriors need special handling to ensure loot is always placed correctly for pre and post floating y saves // Dungeons are not involved with floating y and don't need any changes WorldContext lootContext = GetLootWorldContext(loot); if (lootContext == WorldContext.Interior) { RestoreInteriorPositionHandler(loot, data, lootContext); } else if (lootContext == WorldContext.Exterior) { RestoreExteriorPositionHandler(loot, data, lootContext); } else { loot.transform.position = data.currentPosition; } // Restore appearance if (MeshReplacement.SwapCustomFlatGameobject(data.textureArchive, data.textureRecord, loot.transform, Vector3.zero, lootContext == WorldContext.Dungeon)) { // Use imported model instead of billboard if (billboard) { Destroy(billboard); } Destroy(GetComponent <MeshRenderer>()); } else if (billboard) { // Restore billboard appearance if present billboard.SetMaterial(data.textureArchive, data.textureRecord); } } // Restore items loot.Items.DeserializeItems(data.items); // Restore other data loot.ContainerType = data.containerType; loot.ContainerImage = data.containerImage; loot.TextureArchive = data.textureArchive; loot.TextureRecord = data.textureRecord; loot.stockedDate = data.stockedDate; loot.playerOwned = data.playerOwned; loot.customDrop = data.customDrop; loot.entityName = data.entityName; loot.isEnemyClass = data.isEnemyClass; // Remove loot container if empty if (loot.Items.Count == 0) { GameObjectHelper.RemoveLootContainer(loot); } }