protected override void Save(string rawPath, Hashing.Hash128 hash, Dictionary <string, string> materialNameMapping, Dictionary <string, object> tags) { using (PoolManager.Get(out HashSet <string> shapeNames)) { if (tags.TryGetValue(MyImporterConstants.TAG_HAVOK_DESTRUCTION, out var destructionBufferIn) || tags.TryGetValue(MyImporterConstants.TAG_HAVOK_COLLISION_GEOMETRY, out destructionBufferIn)) { using (var hkWorld = new HkWorld(true, 50000, float.MaxValue, false, 4, 0.6f)) { hkWorld.MarkForWrite(); hkWorld.DestructionWorld = new HkdWorld(hkWorld); hkWorld.UnmarkForWrite(); using (var destructionStorage = new HkDestructionStorage(hkWorld.DestructionWorld)) { var result = destructionStorage.LoadDestructionDataFromBuffer((byte[])destructionBufferIn)[0]; CollectShapeNamesRecursive(result, shapeNames); // RenameShapesRecursive(result, "_" + hash); // var rawOutput = Path.GetTempFileName(); // destructionStorage.SaveDestructionData(result, rawOutput); // var rawBytes = File.ReadAllBytes(rawOutput); // FixupMaterialReferences(rawBytes, materialNameMapping); // tags[MyImporterConstants.TAG_HAVOK_DESTRUCTION] = rawBytes; // File.Delete(rawOutput); } } } if (tags.TryGetValue(MyImporterConstants.TAG_HAVOK_COLLISION_GEOMETRY, out var havokCollision)) { using (PoolManager.Get(out Dictionary <string, string> dict)) { foreach (var kv in materialNameMapping) { dict[kv.Key] = kv.Value; } foreach (var name in shapeNames) { var hb = Hashing.Builder(); hb.Add(name); hb.Add(hash); dict[name] = hb.Build().ToLimitedString(name.Length); } FixupMaterialReferences((byte[])havokCollision, dict); } } } var fullPath = Path.Combine(_exportContentRoot, rawPath); using (var writer = new BinaryWriter(File.Open(fullPath, FileMode.Create))) ModelExporter.ExportModelData(writer, tags); }
public void LoadModelDestruction(MyPhysicalModelDefinition modelDef, bool dontCreateFracturePieces, Vector3 defaultSize, bool destructionRequired = true, bool useShapeVolume = false) { var model = MyModels.GetModelOnlyData(modelDef.Model); var material = modelDef.PhysicalMaterial; if (model != null) { bool forceCollisionsInsteadDestruction = false; //if (model.AssetName.Contains("StoneBattlementAdvancedStraightTop")) //{ //} model.LoadUV = true; HkdBreakableShape bShape; bool createPieceData = false; bool recalculateMass = false; bool registerShape = false; //TODO: Dynamic fracturing is workaround. We need to find the way how to serialize fractured model directly into hkt // and then load it from BreakableShapes. if (model.ModelFractures != null) { if (model.HavokCollisionShapes != null && model.HavokCollisionShapes.Length > 0) { CreateBreakableShapeFromCollisionShapes(model, defaultSize, modelDef); var physicsMesh = CreatePhysicsMesh(model); Storage.RegisterShapeWithGraphics(physicsMesh, model.HavokBreakableShapes[0], modelDef.Id.SubtypeName); string modPath = null; if (Path.IsPathRooted(model.AssetName)) { modPath = model.AssetName.Remove(model.AssetName.LastIndexOf("Models")); } FractureBreakableShape(model.HavokBreakableShapes[0], model.ModelFractures, modPath); recalculateMass = true; registerShape = true; createPieceData = true; } } else if (model.HavokDestructionData != null && !forceCollisionsInsteadDestruction) { try { //string dump = Storage.DumpDestructionData(model.HavokDestructionData); if (model.HavokBreakableShapes == null) //models are cached between sessions { model.HavokBreakableShapes = Storage.LoadDestructionDataFromBuffer(model.HavokDestructionData); createPieceData = true; recalculateMass = true; registerShape = true; } } catch { model.HavokBreakableShapes = null; } } model.HavokDestructionData = null; //we dont need to hold the byte data after loading shape model.HavokData = null; if (model.HavokBreakableShapes == null && destructionRequired) { MyLog.Default.WriteLine(model.AssetName + " does not have destruction data"); CreateBreakableShapeFromCollisionShapes(model, defaultSize, modelDef); recalculateMass = true; registerShape = true; if (MyFakes.SHOW_MISSING_DESTRUCTION && destructionRequired) { //Show missing destructions in pink VRageRender.MyRenderProxy.ChangeModelMaterial(model.AssetName, "Debug"); } } if (model.HavokBreakableShapes == null) { MyLog.Default.WriteLine(string.Format("Model {0} - Unable to load havok destruction data", model.AssetName), LoggingOptions.LOADING_MODELS); return; } System.Diagnostics.Debug.Assert(model.HavokBreakableShapes.Length > 0, "Incomplete destruction data"); bShape = model.HavokBreakableShapes[0]; //bShape.GetChildren(m_tmpChildrenList); //if (m_tmpChildrenList.Count == 0) // bShape.UserObject = (uint)HkdBreakableShape.Flags.FRACTURE_PIECE; if (dontCreateFracturePieces) { bShape.SetFlagRecursively(HkdBreakableShape.Flags.DONT_CREATE_FRACTURE_PIECE); } //m_tmpChildrenList.Clear(); if (registerShape) { bShape.AddReference(); Storage.RegisterShape( bShape, modelDef.Id.SubtypeName ); } // Necessary, otherwise materials on fractures would be missing VRageRender.MyRenderProxy.PreloadMaterials(model.AssetName); if (createPieceData) { CreatePieceData(model, bShape); } if (recalculateMass) { var volume = bShape.CalculateGeometryVolume(); if (volume <= 0 || useShapeVolume) { volume = bShape.Volume; } var realMass = volume * material.Density; if (bShape.Name == "House Half Timber Triangle") { } System.Diagnostics.Debug.Assert(realMass > 0, "Invalid mass data"); bShape.SetMassRecursively(MyDestructionHelper.MassToHavok(realMass)); } //Debug.Assert(CheckVolumeMassRec(bShape, 0.00001f, 0.01f), "Low volume or mass." + bShape.Name); DisableRefCountRec(bShape); if (MyFakes.LAZY_LOAD_DESTRUCTION) { BlockShapePool.AllocateForDefinition(modelDef, MyBlockShapePool.PREALLOCATE_COUNT); } } else { //No armor in ME! } }