/// <summary> /// Adds action door to scene. /// </summary> private static GameObject AddActionDoor(DaggerfallUnity dfUnity, uint modelId, DFBlock.RdbObject obj, Transform parent) { if (dfUnity.Option_DungeonDoorPrefab == null) { return(null); } // Get model data and matrix ModelData modelData; dfUnity.MeshReader.GetModelData(modelId, out modelData); Matrix4x4 modelMatrix = GetModelMatrix(obj); // Instantiate door prefab and add model GameObject go = GameObjectHelper.InstantiatePrefab(dfUnity.Option_DungeonDoorPrefab.gameObject, string.Empty, parent, Vector3.zero); GameObjectHelper.CreateDaggerfallMeshGameObject(modelId, parent, 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; } // Get rotation angle for each axis float degreesX = -obj.Resources.ModelResource.XRotation / BlocksFile.RotationDivisor; float degreesY = -obj.Resources.ModelResource.YRotation / BlocksFile.RotationDivisor; float degreesZ = -obj.Resources.ModelResource.ZRotation / BlocksFile.RotationDivisor; // Apply transforms go.transform.Rotate(0, degreesY, 0, Space.World); go.transform.Rotate(degreesX, 0, 0, Space.World); go.transform.Rotate(0, 0, degreesZ, Space.World); go.transform.localPosition = modelMatrix.GetColumn(3); return(go); }
private void FixEnemyStanding(GameObject go) { Component[] mobiles = go.GetComponentsInChildren(typeof(DaggerfallMobileUnit)); if (mobiles == null) { return; } foreach (DaggerfallMobileUnit enemy in mobiles) { // Don't change for flying enemies if (enemy.Summary.Enemy.Behaviour == MobileBehaviour.Flying) { continue; } // Align to ground Vector2 size = enemy.Summary.RecordSizes[0]; GameObjectHelper.AlignBillboardToGround(enemy.transform.parent.gameObject, size); } }
private static void AddEnemy( DFBlock.RdbObject obj, MobileTypes type, Transform parent = null, long loadID = 0) { // Get default reaction MobileReactions reaction = MobileReactions.Hostile; if (obj.Resources.FlatResource.Action == (int)DFBlock.EnemyReactionTypes.Passive) { reaction = MobileReactions.Passive; } // Just setup demo enemies at this time string name = string.Format("DaggerfallEnemy [{0}]", type.ToString()); Vector3 position = new Vector3(obj.XPos, -obj.YPos, obj.ZPos) * MeshReader.GlobalScale; GameObject go = GameObjectHelper.InstantiatePrefab(DaggerfallUnity.Instance.Option_EnemyPrefab.gameObject, name, parent, position); SetupDemoEnemy setupEnemy = go.GetComponent <SetupDemoEnemy>(); if (setupEnemy != null) { // Configure enemy setupEnemy.ApplyEnemySettings(type, reaction); // Align non-flying units with ground DaggerfallMobileUnit mobileUnit = setupEnemy.GetMobileBillboardChild(); if (mobileUnit.Summary.Enemy.Behaviour != MobileBehaviour.Flying) { GameObjectHelper.AlignControllerToGround(go.GetComponent <CharacterController>()); } } DaggerfallEnemy enemy = go.GetComponent <DaggerfallEnemy>(); if (enemy) { enemy.LoadID = loadID; } }
private static GameObject AddStandaloneModel( DaggerfallUnity dfUnity, ref ModelData modelData, Matrix4x4 matrix, Transform parent) { uint modelID = (uint)modelData.DFMesh.ObjectId; // Add GameObject GameObject go = GameObjectHelper.CreateDaggerfallMeshGameObject(modelID, parent, dfUnity.Option_SetStaticFlags); go.transform.position = matrix.GetColumn(3); go.transform.rotation = GameObjectHelper.QuaternionFromMatrix(matrix); // Is this a city gate? if (IsCityGate(modelID)) { go.AddComponent <DaggerfallCityGate>(); } return(go); }
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(0, -obj.YRotation / BlocksFile.RotationDivisor, 0); Matrix4x4 modelMatrix = 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, 0, modelMatrix)); } // Add or combine if (combiner == null) { AddStandaloneModel(dfUnity, ref modelData, modelMatrix, parent); } else { combiner.Add(ref modelData, modelMatrix); } } }
private void AddEnemy(DFBlock.RdbObject obj, MobileTypes type) { // Get default reaction MobileReactions reaction = MobileReactions.Hostile; if (obj.Resources.FlatResource.FlatData.Reaction == (int)DFBlock.EnemyReactionTypes.Passive) { reaction = MobileReactions.Passive; } // Spawn enemy gameobject GameObject go = GameObjectHelper.CreateDaggerfallEnemyGameObject(type, enemiesNode.transform, reaction); if (go == null) { return; } // Set transform Vector3 enemyPosition = new Vector3(obj.XPos, -obj.YPos, obj.ZPos) * MeshReader.GlobalScale; go.transform.position = enemyPosition; }
private void AddRDBLight(DFBlock.RdbObject obj, Transform parent) { // Do nothing if import option not enabled if (!dfUnity.Option_ImportPointLights) { return; } // Spawn light gameobject float radius = obj.Resources.LightResource.Radius * MeshReader.GlobalScale; GameObject go = GameObjectHelper.CreateDaggerfallRDBPointLight(radius, parent); Vector3 lightPosition = new Vector3(obj.XPos, -obj.YPos, obj.ZPos) * MeshReader.GlobalScale; // Add component DaggerfallLight c = go.AddComponent <DaggerfallLight>(); if (dfUnity.Option_AnimatedPointLights) { c.Animate = true; } // Set transform go.transform.position = lightPosition; }
public static void AddBlockFlats( DaggerfallUnity dfUnity, ref DFBlock blockData, Transform parent = null) { // Add block flats foreach (DFBlock.RmbBlockFlatObjectRecord obj in blockData.RmbBlock.MiscFlatObjectRecords) { // Spawn billboard gameobject GameObject go = GameObjectHelper.CreateDaggerfallBillboardGameObject(obj.TextureArchive, obj.TextureRecord, parent); go.transform.position = new Vector3( obj.XPos, -obj.YPos + BlockFlatsOffsetY, obj.ZPos + BlocksFile.RMBDimension) * MeshReader.GlobalScale; // Add lights if (obj.TextureArchive == 210 && dfUnity.Option_ImportPointLights) { // Spawn light gameobject Vector2 size = dfUnity.MeshReader.GetScaledBillboardSize(210, obj.TextureRecord); GameObject lightgo = GameObjectHelper.CreateDaggerfallRMBPointLight(go.transform); lightgo.transform.position = new Vector3( obj.XPos, -obj.YPos + size.y, obj.ZPos + BlocksFile.RMBDimension) * MeshReader.GlobalScale; // Animate light DaggerfallLight c = lightgo.AddComponent <DaggerfallLight>(); c.ParentBillboard = go.GetComponent <DaggerfallBillboard>(); if (dfUnity.Option_AnimatedPointLights) { c.Animate = true; } } } }
/// <summary> /// Instantiate base RMB block by DFBlock data. /// </summary> /// <param name="blockData">Block data.</param> /// <param name="layoutX">X coordinate in parent map layout.</param> /// /// <param name="layoutY">Y coordinate in parent map layout.</param> /// <param name="cloneFrom">Prefab to clone from.</param> /// <returns>Block GameObject.</returns> public static GameObject CreateBaseGameObject(ref DFBlock blockData, int layoutX, int layoutY, DaggerfallRMBBlock cloneFrom = null) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return(null); } // Create gameobject GameObject go; string name = string.Format("DaggerfallBlock [{0}]", blockData.Name); if (cloneFrom != null) { go = GameObjectHelper.InstantiatePrefab(cloneFrom.gameObject, name, null, Vector3.zero); } else { go = new GameObject(name); } // Setup combiner ModelCombiner combiner = null; if (dfUnity.Option_CombineRMB) { combiner = new ModelCombiner(); } // Lists to receive any doors found in this block List <StaticDoor> modelDoors; List <StaticDoor> propDoors; List <StaticBuilding> modelBuildings; // Add models and static props GameObject modelsNode = new GameObject("Models"); modelsNode.transform.parent = go.transform; AddModels(dfUnity, layoutX, layoutY, ref blockData, out modelDoors, out modelBuildings, combiner, modelsNode.transform); AddProps(dfUnity, ref blockData, out propDoors, combiner, modelsNode.transform); // Combine list of doors found in models and props List <StaticDoor> allDoors = new List <StaticDoor>(); if (modelDoors.Count > 0) { allDoors.AddRange(modelDoors); } if (propDoors.Count > 0) { allDoors.AddRange(propDoors); } // Assign building key to each door for (int i = 0; i < allDoors.Count; i++) { StaticDoor door = allDoors[i]; door.buildingKey = BuildingDirectory.MakeBuildingKey((byte)layoutX, (byte)layoutY, (byte)door.recordIndex); allDoors[i] = door; } // Assign building key to each building for (int i = 0; i < modelBuildings.Count; i++) { StaticBuilding building = modelBuildings[i]; building.buildingKey = BuildingDirectory.MakeBuildingKey((byte)layoutX, (byte)layoutY, (byte)building.recordIndex); modelBuildings[i] = building; } // Add static doors component if (allDoors.Count > 0) { AddStaticDoors(allDoors.ToArray(), go); } // Add static buildings component if (modelBuildings.Count > 0) { AddStaticBuildings(modelBuildings.ToArray(), go); } // Apply combiner if (combiner != null) { if (combiner.VertexCount > 0) { combiner.Apply(); GameObjectHelper.CreateCombinedMeshGameObject( combiner, "CombinedModels", modelsNode.transform, dfUnity.Option_SetStaticFlags); } } return(go); }
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++; } }
/// <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; } if (!TextureReplacement.CustomTextureExist(obj.TextureArchive, obj.TextureRecord)) { // 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 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> /// Adds models and their actions to scene. /// </summary> private static void AddModels( DaggerfallUnity dfUnity, ref DFBlock blockData, int[] textureTable, bool allowExitDoors, out List <StaticDoor> exitDoorsOut, ModelCombiner combiner = null, Transform modelsParent = null, Transform actionModelsParent = null) { exitDoorsOut = new List <StaticDoor>(); // Action record linkages Dictionary <int, ActionLink> actionLinkDict = new Dictionary <int, ActionLink>(); // Iterate object groups int groupIndex = 0; foreach (DFBlock.RdbObjectRoot group in blockData.RdbBlock.ObjectRootList) { // Skip empty object groups if (null == group.RdbObjects) { groupIndex++; continue; } // Iterate objects in this group foreach (DFBlock.RdbObject obj in group.RdbObjects) { // Add models if (obj.Type == DFBlock.RdbResourceTypes.Model) { // Get model reference index and id int modelReference = obj.Resources.ModelResource.ModelIndex; uint modelId = blockData.RdbBlock.ModelReferenceList[modelReference].ModelIdNum; // Filter exit door models where flag not set if (modelId == exitDoorModelID && !allowExitDoors) { continue; } // Filter action door models // These must be added by AddActionDoors() if (IsActionDoor(ref blockData, obj, modelReference)) { continue; } // Get matrix Matrix4x4 modelMatrix = GetModelMatrix(obj); // Get model data ModelData modelData; dfUnity.MeshReader.GetModelData(modelId, out modelData); // Add to static doors exitDoorsOut.AddRange(GameObjectHelper.GetStaticDoors(ref modelData, blockData.Index, 0, modelMatrix)); // Check if model has an action record bool hasAction = HasAction(obj); // Special handling for tapestries and banners // Some of these are so far out from wall player can become stuck behind them // Adding model invidually without collider to avoid problem // Not sure if these object ever actions, but bypass this hack if they do if (modelId >= minTapestryID && modelId <= maxTapestryID && !hasAction) { AddStandaloneModel(dfUnity, ref modelData, modelMatrix, modelsParent, hasAction, true); continue; } // Add or combine GameObject standaloneObject = null; Transform parent = (hasAction) ? actionModelsParent : modelsParent; if (combiner == null || hasAction) { standaloneObject = AddStandaloneModel(dfUnity, ref modelData, modelMatrix, parent, hasAction); standaloneObject.GetComponent <DaggerfallMesh>().SetDungeonTextures(textureTable); } else { combiner.Add(ref modelData, modelMatrix); } // Add action if (hasAction && standaloneObject != null) { AddAction(standaloneObject, blockData, obj, modelReference, groupIndex, actionLinkDict); } } } // Increment group index groupIndex++; } // Link action nodes LinkActionNodes(actionLinkDict); }
/// <summary> /// Instantiate base RDB block by DFBlock data. /// </summary> /// <param name="blockData">Block data.</param> /// <param name="textureTable">Optional texture table for dungeon.</param> /// <param name="allowExitDoors">Add exit doors to block.</param> /// <param name="cloneFrom">Clone and build on a prefab object template.</param> /// <returns>Block GameObject.</returns> public static GameObject CreateBaseGameObject( ref DFBlock blockData, int[] textureTable = null, bool allowExitDoors = true, DaggerfallRDBBlock cloneFrom = null) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return(null); } // Use default texture table if one not specified if (textureTable == null) { textureTable = StaticTextureTables.DefaultTextureTable; } // Create gameobject GameObject go; string name = string.Format("DaggerfallBlock [{0}]", blockData.Name); if (cloneFrom != null) { go = GameObjectHelper.InstantiatePrefab(cloneFrom.gameObject, name, null, Vector3.zero); } else { go = new GameObject(name); go.AddComponent <DaggerfallRDBBlock>(); } // Setup combiner ModelCombiner combiner = null; if (dfUnity.Option_CombineRDB) { combiner = new ModelCombiner(); } // Add parent node GameObject modelsNode = new GameObject("Models"); GameObject actionModelsNode = new GameObject("Action Models"); modelsNode.transform.parent = go.transform; actionModelsNode.transform.parent = go.transform; // Add models List <StaticDoor> exitDoors; AddModels( dfUnity, ref blockData, textureTable, allowExitDoors, out exitDoors, combiner, modelsNode.transform, actionModelsNode.transform); // Apply combiner if (combiner != null) { if (combiner.VertexCount > 0) { combiner.Apply(); GameObject cgo = GameObjectHelper.CreateCombinedMeshGameObject( combiner, "CombinedModels", modelsNode.transform, dfUnity.Option_SetStaticFlags); cgo.GetComponent <DaggerfallMesh>().SetDungeonTextures(textureTable); } } // Add exit doors if (exitDoors.Count > 0) { DaggerfallStaticDoors c = go.AddComponent <DaggerfallStaticDoors>(); c.Doors = exitDoors.ToArray(); } return(go); }
/// <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); } } }
/// <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); } } }
private void AddRDBModel(DFBlock.RdbObject obj, out List <StaticDoor> doorsOut, Transform parent) { bool overrideCombine = false; doorsOut = new List <StaticDoor>(); // Get model reference index and id int modelReference = obj.Resources.ModelResource.ModelIndex; uint modelId = blockData.RdbBlock.ModelReferenceList[modelReference].ModelIdNum; // Get rotation angle for each axis float degreesX = -obj.Resources.ModelResource.XRotation / BlocksFile.RotationDivisor; float degreesY = -obj.Resources.ModelResource.YRotation / BlocksFile.RotationDivisor; float degreesZ = -obj.Resources.ModelResource.ZRotation / BlocksFile.RotationDivisor; // Calcuate transform Vector3 position = new Vector3(obj.XPos, -obj.YPos, obj.ZPos) * MeshReader.GlobalScale; // Calculate matrix Vector3 rx = new Vector3(degreesX, 0, 0); Vector3 ry = new Vector3(0, degreesY, 0); Vector3 rz = new Vector3(0, 0, degreesZ); Matrix4x4 modelMatrix = Matrix4x4.identity; modelMatrix *= Matrix4x4.TRS(position, Quaternion.identity, Vector3.one); modelMatrix *= Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(rz), Vector3.one); modelMatrix *= Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(rx), Vector3.one); modelMatrix *= Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(ry), Vector3.one); // Get model data ModelData modelData; dfUnity.MeshReader.GetModelData(modelId, out modelData); // Discard static exit doors not in starting block // Exit doors are just a model slapped over wall or doorway // This allows Daggerfall to toggle exits on and off if (modelData.DFMesh.ObjectId == 70300) { if (!isStartingBlock) { return; } } // Doors - there are no working static building doors inside dungeons, just the exits and action doors bool isActionDoor = IsActionDoor(blockData, obj, modelReference); if (isActionDoor) { parent = doorsNode.transform; } else if (modelData.Doors != null) { doorsOut.AddRange(GameObjectHelper.GetStaticDoors(ref modelData, blockData.Index, 0, modelMatrix)); } // Action records bool hasAction = HasAction(blockData, obj, modelReference); if (hasAction) { parent = actionModelsNode.transform; } // Flags bool isStatic = dfUnity.Option_SetStaticFlags; if (isActionDoor || hasAction) { // Moving objects are never static or combined isStatic = false; overrideCombine = true; } // Add to scene if (dfUnity.Option_CombineRDB && !overrideCombine) { combiner.Add(ref modelData, modelMatrix); } else { // Spawn mesh gameobject GameObject go = GameObjectHelper.CreateDaggerfallMeshGameObject(modelId, parent, isStatic); go.GetComponent <DaggerfallMesh>().SetDungeonTextures(textureTable); // Apply transforms go.transform.Rotate(0, degreesY, 0, Space.World); go.transform.Rotate(degreesX, 0, 0, Space.World); go.transform.Rotate(0, 0, degreesZ, Space.World); go.transform.localPosition = position; // Add action door if (isActionDoor) { DaggerfallActionDoor dfActionDoor = go.AddComponent <DaggerfallActionDoor>(); // Add action door audio if (dfUnity.Option_DefaultSounds) { AddActionDoorAudioSource(go); dfActionDoor.SetDungeonDoorSounds(); } } // Add action component if (hasAction && !isActionDoor) { AddAction(go, blockData, obj, modelReference); } } }
private void AddRDBFlat(DFBlock.RdbObject obj, Transform parent) { int archive = obj.Resources.FlatResource.TextureArchive; int record = obj.Resources.FlatResource.TextureRecord; // Spawn billboard gameobject GameObject go = GameObjectHelper.CreateDaggerfallBillboardGameObject(archive, record, parent, true); Vector3 billboardPosition = new Vector3(obj.XPos, -obj.YPos, obj.ZPos) * MeshReader.GlobalScale; // Add RDB data to billboard DaggerfallBillboard dfBillboard = go.GetComponent <DaggerfallBillboard>(); dfBillboard.SetResourceData(obj.Resources.FlatResource); // Set transform go.transform.position = billboardPosition; // Handle supported editor flats if (archive == 199) { switch (record) { case 10: // Start marker startMarkers.Add(go); break; case 15: // Random enemy if (dfUnity.Option_ImportEnemies) { AddRandomRDBEnemy(obj); go.SetActive(false); } break; case 16: // Fixed enemy if (dfUnity.Option_ImportEnemies) { AddFixedRDBEnemy(obj); go.SetActive(false); } break; } } // Add torch burning sound if (dfUnity.Option_DefaultSounds && archive == 210) { switch (record) { case 0: case 1: case 6: case 16: case 17: case 18: case 19: case 20: AddTorchAudioSource(go); break; } } }
public static GameObject CreateGameObject( DaggerfallUnity dfUnity, ref DFBlock blockData, bool disableGround = false, DaggerfallBillboardBatch natureBatch = null) { // Create gameobject GameObject go = new GameObject(string.Format("DaggerfallBlock [Name={0}]", blockData.Name)); //go.AddComponent<DaggerfallBlock>(); // Setup combiner ModelCombiner combiner = null; if (dfUnity.Option_CombineRMB) { combiner = new ModelCombiner(); } // Lists to receive any doors found in this block List <StaticDoor> modelDoors; List <StaticDoor> propDoors; // Add models and props GameObject modelsNode = new GameObject("Models"); modelsNode.transform.parent = go.transform; AddModels(dfUnity, ref blockData, out modelDoors, combiner, modelsNode.transform); AddProps(dfUnity, ref blockData, out propDoors, combiner, modelsNode.transform); // Add doors List <StaticDoor> allDoors = new List <StaticDoor>(); if (modelDoors.Count > 0) { allDoors.AddRange(modelDoors); } if (propDoors.Count > 0) { allDoors.AddRange(propDoors); } if (allDoors.Count > 0) { AddDoors(allDoors.ToArray(), go); } // Add block flats GameObject flatsNode = new GameObject("Flats"); flatsNode.transform.parent = go.transform; AddBlockFlats(dfUnity, ref blockData, flatsNode.transform); // Add nature flats if (natureBatch == null) { AddNatureFlats(dfUnity, ref blockData, flatsNode.transform); } else { AddNatureFlatsToBatch(natureBatch, ref blockData); } // Add ground plane if (dfUnity.Option_SimpleGroundPlane && !disableGround) { AddSimpleGroundPlane(dfUnity, ref blockData, go.transform); } // Apply combiner if (combiner != null) { if (combiner.VertexCount > 0) { combiner.Apply(); GameObjectHelper.CreateCombinedMeshGameObject( combiner, "CombinedModels", modelsNode.transform, dfUnity.Option_SetStaticFlags); } } return(go); }
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> /// Creates a new RDB GameObject and performs block layout. /// Can pass information about dungeon for texture swaps and random enemies. /// </summary> /// <param name="dfUnity">DaggerfallUnity singleton. Required for content readers and settings.</param> /// <param name="blockName">Name of RDB block to build.</param> /// <param name="isStartingBlock">True if this is the starting block. Controls exit doors.</param> /// <param name="textureTable">Dungeon texture table.</param> /// <param name="dungeonType">Type of dungeon for random encounter tables.</param> /// <param name="seed">Seed for random encounters.</param> /// <returns>GameObject.</returns> public static GameObject CreateGameObject( string blockName, bool isStartingBlock, int[] textureTable = null, DFRegion.DungeonTypes dungeonType = DFRegion.DungeonTypes.HumanStronghold, int seed = 0) { // Validate if (string.IsNullOrEmpty(blockName)) { return(null); } if (!blockName.ToUpper().EndsWith(".RDB")) { return(null); } DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return(null); } // Create gameobject GameObject go = new GameObject(string.Format("DaggerfallBlock [Name={0}]", blockName)); DaggerfallRDBBlock dfBlock = go.AddComponent <DaggerfallRDBBlock>(); // Start new layout RDBLayout layout = new RDBLayout(blockName); layout.isStartingBlock = isStartingBlock; layout.textureTable = textureTable; layout.dungeonType = dungeonType; layout.staticModelsNode = new GameObject("Static Models"); layout.actionModelsNode = new GameObject("Action Models"); layout.doorsNode = new GameObject("Doors"); layout.flatsNode = new GameObject("Flats"); layout.lightsNode = new GameObject("Lights"); layout.enemiesNode = new GameObject("Enemies"); // Parent child game objects layout.staticModelsNode.transform.parent = go.transform; layout.actionModelsNode.transform.parent = go.transform; layout.doorsNode.transform.parent = go.transform; layout.flatsNode.transform.parent = go.transform; layout.lightsNode.transform.parent = go.transform; layout.enemiesNode.transform.parent = go.transform; // List to receive any exit doors found List <StaticDoor> allDoors = new List <StaticDoor>(); // Seed random generator UnityEngine.Random.seed = seed; // Iterate object groups layout.groupIndex = 0; DFBlock blockData = dfUnity.ContentReader.BlockFileReader.GetBlock(blockName); foreach (DFBlock.RdbObjectRoot group in blockData.RdbBlock.ObjectRootList) { // Skip empty object groups if (null == group.RdbObjects) { layout.groupIndex++; continue; } // Iterate objects in this group List <StaticDoor> modelDoors; foreach (DFBlock.RdbObject obj in group.RdbObjects) { // Handle by object type switch (obj.Type) { case DFBlock.RdbResourceTypes.Model: layout.AddRDBModel(obj, out modelDoors, layout.staticModelsNode.transform); if (modelDoors.Count > 0) { allDoors.AddRange(modelDoors); } break; case DFBlock.RdbResourceTypes.Flat: layout.AddRDBFlat(obj, layout.flatsNode.transform); break; case DFBlock.RdbResourceTypes.Light: layout.AddRDBLight(obj, layout.lightsNode.transform); break; default: break; } } // Increment group index layout.groupIndex++; } // Link action nodes layout.LinkActionNodes(); // Combine meshes if (dfUnity.Option_CombineRDB) { layout.combiner.Apply(); GameObject cgo = GameObjectHelper.CreateCombinedMeshGameObject(layout.combiner, "CombinedMeshes", layout.staticModelsNode.transform, dfUnity.Option_SetStaticFlags); cgo.GetComponent <DaggerfallMesh>().SetDungeonTextures(textureTable); } // Fix enemy standing positions for this block // Some enemies are floating in air or sunk into ground // Can only adjust this after geometry instantiated layout.FixEnemyStanding(go); // Store start markers in block dfBlock.SetStartMarkers(layout.startMarkers.ToArray()); // Add doors if (allDoors.Count > 0) { layout.AddDoors(allDoors.ToArray(), go); } return(go); }
/// <summary> /// Instantiate base RMB block by DFBlock data. /// </summary> /// <param name="blockData">Block data.</param> /// <returns>Block GameObject.</returns> public static GameObject CreateBaseGameObject(ref DFBlock blockData, DaggerfallRMBBlock cloneFrom = null) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return(null); } // Create gameobject GameObject go; string name = string.Format("DaggerfallBlock [{0}]", blockData.Name); if (cloneFrom != null) { go = GameObjectHelper.InstantiatePrefab(cloneFrom.gameObject, name, null, Vector3.zero); } else { go = new GameObject(name); go.AddComponent <DaggerfallRMBBlock>(); } // Setup combiner ModelCombiner combiner = null; if (dfUnity.Option_CombineRMB) { combiner = new ModelCombiner(); } // Lists to receive any doors found in this block List <StaticDoor> modelDoors; List <StaticDoor> propDoors; // Add models and static props GameObject modelsNode = new GameObject("Models"); modelsNode.transform.parent = go.transform; AddModels(dfUnity, ref blockData, out modelDoors, combiner, modelsNode.transform); AddProps(dfUnity, ref blockData, out propDoors, combiner, modelsNode.transform); // Add doors List <StaticDoor> allDoors = new List <StaticDoor>(); if (modelDoors.Count > 0) { allDoors.AddRange(modelDoors); } if (propDoors.Count > 0) { allDoors.AddRange(propDoors); } if (allDoors.Count > 0) { AddStaticDoors(allDoors.ToArray(), go); } // Apply combiner if (combiner != null) { if (combiner.VertexCount > 0) { combiner.Apply(); GameObjectHelper.CreateCombinedMeshGameObject( combiner, "CombinedModels", modelsNode.transform, dfUnity.Option_SetStaticFlags); } } return(go); }
/// <summary> /// Layout a complete RMB block game object. /// Can be used standalone or as part of a city build. /// </summary> public static GameObject CreateRMBBlockGameObject( string blockName, bool addGroundPlane = true, DaggerfallRMBBlock cloneFrom = null, DaggerfallBillboardBatch natureBillboardBatch = null, DaggerfallBillboardBatch lightsBillboardBatch = null, DaggerfallBillboardBatch animalsBillboardBatch = null, TextureAtlasBuilder miscBillboardAtlas = null, DaggerfallBillboardBatch miscBillboardBatch = null, ClimateNatureSets climateNature = ClimateNatureSets.TemperateWoodland, ClimateSeason climateSeason = ClimateSeason.Summer) { // Get DaggerfallUnity DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return(null); } // Create base object DFBlock blockData; GameObject go = RMBLayout.CreateBaseGameObject(blockName, out blockData, cloneFrom); // Create flats node GameObject flatsNode = new GameObject("Flats"); flatsNode.transform.parent = go.transform; // Create lights node GameObject lightsNode = new GameObject("Lights"); lightsNode.transform.parent = go.transform; // If billboard batching is enabled but user has not specified // a batch, then make our own auto batch for this block bool autoLightsBatch = false; bool autoNatureBatch = false; bool autoAnimalsBatch = false; if (dfUnity.Option_BatchBillboards) { if (natureBillboardBatch == null) { autoNatureBatch = true; int natureArchive = ClimateSwaps.GetNatureArchive(climateNature, climateSeason); natureBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(natureArchive, flatsNode.transform); } if (lightsBillboardBatch == null) { autoLightsBatch = true; lightsBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(TextureReader.LightsTextureArchive, flatsNode.transform); } if (animalsBillboardBatch == null) { autoAnimalsBatch = true; animalsBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(TextureReader.AnimalsTextureArchive, flatsNode.transform); } } // Layout light billboards and gameobjects RMBLayout.AddLights(ref blockData, flatsNode.transform, lightsNode.transform, lightsBillboardBatch); // Layout nature billboards RMBLayout.AddNatureFlats(ref blockData, flatsNode.transform, natureBillboardBatch, climateNature, climateSeason); // Layout all other flats RMBLayout.AddMiscBlockFlats(ref blockData, flatsNode.transform, animalsBillboardBatch, miscBillboardAtlas, miscBillboardBatch); // Add ground plane if (addGroundPlane) { RMBLayout.AddGroundPlane(ref blockData, go.transform); } // Apply auto batches if (autoNatureBatch) { natureBillboardBatch.Apply(); } if (autoLightsBatch) { lightsBillboardBatch.Apply(); } if (autoAnimalsBatch) { animalsBillboardBatch.Apply(); } return(go); }