private static void AddModels( DaggerfallUnity dfUnity, ref DFBlock blockData, out List <StaticDoor> doorsOut, ModelCombiner combiner = null, Transform parent = null) { doorsOut = new List <StaticDoor>(); // 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 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)); } // 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> /// Adds another ModelCombiner to this one. /// </summary> /// <param name="other">Other ModelCombiner.</param> /// <param name="matrix">Transform to apply</param> public void Add(ModelCombiner other, Matrix4x4 matrix) { // Do nothing if sealed if (isSealed || other.isSealed) { return; } // Add other items to this builder foreach (var item in other.builderDictionary) { BatchData batchData = item.Value; AddData(item.Key, ref batchData, matrix); } }
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 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); } } }
/// <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> /// <param name="serialize">Allow for serialization of supported sub-objects.</param> /// <returns>Block GameObject.</returns> public static GameObject CreateBaseGameObject( ref DFBlock blockData, Dictionary<int, ActionLink> actionLinkDict, int[] textureTable = null, bool allowExitDoors = true, DaggerfallRDBBlock cloneFrom = null, bool serialize = true) { 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, actionLinkDict, textureTable, allowExitDoors, out exitDoors, serialize, 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; }
public static GameObject CreateCombinedMeshGameObject( ModelCombiner combiner, string meshName, Transform parent, bool makeStatic = false) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; // Create gameobject GameObject go = new GameObject(meshName); if (parent) { go.transform.parent = parent; } // Assign components DaggerfallMesh dfMesh = go.AddComponent <DaggerfallMesh>(); MeshFilter meshFilter = go.GetComponent <MeshFilter>(); MeshRenderer meshRenderer = go.GetComponent <MeshRenderer>(); // Assign mesh and materials CachedMaterial[] cachedMaterials; int[] textureKeys; bool hasAnimations; Mesh mesh = dfUnity.MeshReader.GetCombinedMesh( dfUnity, combiner, out cachedMaterials, out textureKeys, out hasAnimations, dfUnity.MeshReader.AddMeshTangents, dfUnity.MeshReader.AddMeshLightmapUVs); // Assign animated materials component if required if (hasAnimations) { AssignAnimateTextureComponent(cachedMaterials, go); } // Assign mesh and materials array if (mesh) { meshFilter.sharedMesh = mesh; meshRenderer.sharedMaterials = GetMaterialArray(cachedMaterials); dfMesh.SetDefaultTextures(textureKeys); } // Assign collider if (dfUnity.Option_AddMeshColliders) { MeshCollider collider = go.AddComponent <MeshCollider>(); collider.sharedMesh = mesh; } // Assign static if (makeStatic) { go.isStatic = true; } 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> /// 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> /// Adds models and their actions to scene. /// </summary> private static void AddModels( DaggerfallUnity dfUnity, ref DFBlock blockData, Dictionary<int, ActionLink> actionLinkDict, int[] textureTable, bool allowExitDoors, out List<StaticDoor> exitDoorsOut, bool serialize, ModelCombiner combiner = null, Transform modelsParent = null, Transform actionModelsParent = null) { exitDoorsOut = new List<StaticDoor>(); // Iterate object groups foreach (DFBlock.RdbObjectRoot group in blockData.RdbBlock.ObjectRootList) { // Skip empty object groups if (null == group.RdbObjects) { 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) AddActionModelHelper(standaloneObject, actionLinkDict, obj, ref blockData, serialize); } } } }
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 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); } }
public static GameObject CreateCombinedMeshGameObject( ModelCombiner combiner, string meshName, Transform parent, bool makeStatic = false) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; // Create gameobject GameObject go = new GameObject(meshName); if (parent) go.transform.parent = parent; // Assign components DaggerfallMesh dfMesh = go.AddComponent<DaggerfallMesh>(); MeshFilter meshFilter = go.GetComponent<MeshFilter>(); MeshRenderer meshRenderer = go.GetComponent<MeshRenderer>(); // Assign mesh and materials CachedMaterial[] cachedMaterials; int[] textureKeys; bool hasAnimations; Mesh mesh = dfUnity.MeshReader.GetCombinedMesh( dfUnity, combiner, out cachedMaterials, out textureKeys, out hasAnimations, dfUnity.MeshReader.AddMeshTangents, dfUnity.MeshReader.AddMeshLightmapUVs); // Assign animated materials component if required if (hasAnimations) AssignAnimatedMaterialComponent(cachedMaterials, go); // Assign mesh and materials array if (mesh) { meshFilter.sharedMesh = mesh; meshRenderer.sharedMaterials = GetMaterialArray(cachedMaterials); dfMesh.SetDefaultTextures(textureKeys); } // Assign collider if (dfUnity.Option_AddMeshColliders) { MeshCollider collider = go.AddComponent<MeshCollider>(); collider.sharedMesh = mesh; } // Assign static if (makeStatic) go.isStatic = true; return go; }
private static void AddModels( DaggerfallUnity dfUnity, ref DFBlock blockData, out List<StaticDoor> doorsOut, ModelCombiner combiner = null, Transform parent = null) { doorsOut = new List<StaticDoor>(); // 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 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)); // 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> /// 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> /// 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> /// Adds another ModelCombiner to this one. /// </summary> /// <param name="other">Other ModelCombiner.</param> /// <param name="matrix">Transform to apply</param> public void Add(ModelCombiner other, Matrix4x4 matrix) { // Do nothing if sealed if (isSealed || other.isSealed) return; // Add other items to this builder foreach (var item in other.builderDictionary) { BatchData batchData = item.Value; AddData(item.Key, ref batchData, matrix); } }
/// <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> /// Gets Unity Mesh from previously combined model data. /// </summary> /// <param name="dfUnity">DaggerfallUnity singleon for loading content.</param> /// <param name="combiner">ModelCombiner to build from.</param> /// <param name="cachedMaterialsOut">Array of cached materials in order of submesh.</param> /// <param name="textureKeysOut">Array of original texture keys in order of submesh.</param> /// <param name="hasAnimationsOut">True if one or more materials have animations.</param> /// <param name="solveTangents">Solve tangents for this mesh.</param> /// <param name="lightmapUVs">Add secondary lightmap UVs to this mesh.</param> /// <returns>Mesh object or null.</returns> public Mesh GetCombinedMesh( DaggerfallUnity dfUnity, ModelCombiner combiner, out CachedMaterial[] cachedMaterialsOut, out int[] textureKeysOut, out bool hasAnimationsOut, bool solveTangents = false, bool lightmapUVs = false) { cachedMaterialsOut = null; hasAnimationsOut = false; textureKeysOut = null; // Ready check if (!IsReady) return null; // Get combined model ModelCombiner.CombinedModel combinedModel; if (!combiner.GetCombinedModel(out combinedModel)) return null; // Load materials cachedMaterialsOut = new CachedMaterial[combinedModel.SubMeshes.Length]; textureKeysOut = new int[combinedModel.SubMeshes.Length]; for (int i = 0; i < combinedModel.SubMeshes.Length; i++) { int archive = combinedModel.SubMeshes[i].TextureArchive; int record = combinedModel.SubMeshes[i].TextureRecord; textureKeysOut[i] = MaterialReader.MakeTextureKey((short)archive, (byte)record, (byte)0); // Add material to array CachedMaterial cachedMaterial; dfUnity.MaterialReader.GetCachedMaterial(archive, record, 0, out cachedMaterial); cachedMaterialsOut[i] = cachedMaterial; // Set animation flag if (cachedMaterial.singleFrameCount > 1 && !hasAnimationsOut) hasAnimationsOut = true; } // Create mesh Mesh mesh = new Mesh(); mesh.name = "CombinedMesh"; mesh.vertices = combinedModel.Vertices; mesh.normals = combinedModel.Normals; mesh.uv = combinedModel.UVs; mesh.subMeshCount = combinedModel.SubMeshes.Length; // Set submesh triangles for (int s = 0; s < mesh.subMeshCount; s++) { var sub = combinedModel.SubMeshes[s]; int[] triangles = new int[sub.PrimitiveCount * 3]; for (int t = 0; t < sub.PrimitiveCount * 3; t++) { triangles[t] = combinedModel.Indices[sub.StartIndex + t]; } mesh.SetTriangles(triangles, s); } // Finalise mesh if (solveTangents) TangentSolver(mesh); if (lightmapUVs) AddLightmapUVs(mesh); mesh.RecalculateBounds(); mesh.Optimize(); return mesh; }