public static MCMloRoomDef GetRoomForEntity(MCMloArchetypeDef mlo, Vector3[][] roomExtents, MCEntityDef entity) { for (int i = 0; i < roomExtents.Length; i++) { if (mlo.Rooms[i].Name == "limbo") { continue; } var extents = roomExtents[i]; var emin = extents[0]; var emax = extents[1]; var drawable = Program.GetDrawable(entity.ArchetypeName); if (drawable == null) { if ( entity.Position.X >= emin.X && entity.Position.Y >= emin.Y && entity.Position.Z >= emin.Z && entity.Position.X <= emax.X && entity.Position.Y <= emax.Y && entity.Position.Z <= emax.Z ) { return(mlo.Rooms[i]); } } else { Quaternion orientation = new Quaternion(entity.Rotation); Vector3 dcenter = (Vector3)drawable.BoundingCenter; Vector3 dbbmin = (Vector3)(Vector4)drawable.BoundingBoxMin - dcenter; Vector3 dbbmax = (Vector3)(Vector4)drawable.BoundingBoxMax - dcenter; Vector3 center = entity.Position + dcenter; Vector3 bbmin = entity.Position + dbbmin; Vector3 bbmax = entity.Position + dbbmax; Vector3 c1 = Utils.RotateTransform(orientation, bbmin, entity.Position); Vector3 c2 = Utils.RotateTransform(orientation, bbmax, entity.Position); bbmin = Vector3.Min(c1, c2); bbmax = Vector3.Max(c1, c2); if ( center.X >= emin.X && center.Y >= emin.Y && center.Z >= emin.Z && center.X <= emax.X && center.Y <= emax.Y && center.Z <= emax.Z ) { return(mlo.Rooms[i]); } } } return(null); }
/* Specific Utils */ public static void World2Mlo(MCEntityDef entity, MCMloArchetypeDef mlo, Vector3 mloWorldPosition, Quaternion mloWorldRotation) { var objRot = new Quaternion(entity.Rotation.X, entity.Rotation.Y, entity.Rotation.Z, entity.Rotation.W); var rotationDiff = objRot * mloWorldRotation; // Multiply initial entity rotation by mlo rotation rotationDiff.Normalize(); entity.Position -= mloWorldPosition; // Substract mlo world coords from entity world coords entity.Position = Utils.RotateTransform(Quaternion.Invert(mloWorldRotation), entity.Position, Vector3.Zero); // Rotate entity around center of mlo instance (mlo entities rotations in space are inverted) entity.Rotation = new Vector4(rotationDiff.X, rotationDiff.Y, rotationDiff.Z, rotationDiff.W); }
public static void Mlo2World(MCEntityDef entity, MCMloArchetypeDef mlo, Vector3 mloWorldPosition, Quaternion mloWorldRotation) { var objRot = new Quaternion(entity.Rotation.X, entity.Rotation.Y, entity.Rotation.Z, entity.Rotation.W); entity.Position = Utils.RotateTransform(mloWorldRotation, entity.Position, Vector3.Zero); entity.Position += mloWorldPosition; var rotationDiff = objRot * Quaternion.Invert(mloWorldRotation); rotationDiff.Normalize(); entity.Rotation = new Vector4(rotationDiff.X, rotationDiff.Y, rotationDiff.Z, rotationDiff.W); }
static void HandleInjectEntitiesOptions(string[] args) { CommandLine.Parse <InjectEntitiesOptions>(args, (opts, gOpts) => { if (opts.Ymap == null) { Console.WriteLine("Please provide source ymap file with --ymap"); return; } if (opts.Ytyp == null) { Console.WriteLine("Please provide source ytyp file with --ytyp"); return; } if (opts.Position == null || opts.Position.Count() != 3) { Console.WriteLine("Please provide a correct position ex: --position 120.5,1370.312,769.2"); return; } if (opts.Rotation == null || opts.Rotation.Count() != 4) { Console.WriteLine("Plase provide a correct rotation ex: --rotation 0,0,0,1"); return; } if (opts.Name == null) { Console.WriteLine("Plase provide new generated ytyp name with --name"); return; } Init(args); var ymapInfos = Utils.Expand(opts.Ymap); var ymapNames = ymapInfos.Select(e => Path.GetFileNameWithoutExtension(e.Name)).ToArray(); var position = new Vector3(opts.Position.ElementAt(0), opts.Position.ElementAt(1), opts.Position.ElementAt(2)); var rotation = new Quaternion(opts.Rotation.ElementAt(0), opts.Rotation.ElementAt(1), opts.Rotation.ElementAt(2), opts.Rotation.ElementAt(3)); var ytyp = new YtypFile(); ytyp.Load(opts.Ytyp); MCMloArchetypeDef mlo = null; for (int i = 0; i < ytyp.CMapTypes.MloArchetypes.Count; i++) { if (opts.MloName == null) { mlo = ytyp.CMapTypes.MloArchetypes[i]; break; } else { uint mloNameHash = Jenkins.Hash(opts.MloName.ToLowerInvariant()); if (mloNameHash == ytyp.CMapTypes.MloArchetypes[i].Name) { Console.Error.WriteLine("Found MLO => " + opts.MloName); mlo = ytyp.CMapTypes.MloArchetypes[i]; break; } } } if (mlo == null) { Console.WriteLine("MLO archetype not found"); return; } var ymaps = new List <YmapFile>(); for (int i = 0; i < ymapInfos.Length; i++) { var ymap = new YmapFile(); ymap.Load(ymapInfos[i].FullName); ymaps.Add(ymap); } var missingYmap = new YmapFile(); int missingCount = 0; Console.WriteLine("Calculating rooms extents"); var roomExtents = new Vector3[mlo.Rooms.Count][]; for (int i = 0; i < mlo.Rooms.Count; i++) { var room = mlo.Rooms[i]; var entities = new List <MCEntityDef>(); for (int j = 0; j < room.AttachedObjects.Count; j++) { int idx = (int)room.AttachedObjects[j]; if (idx >= mlo.Entities.Count) { continue; } entities.Add(mlo.Entities[idx]); } var extents = Utils.CalcExtents(entities); roomExtents[i] = extents[0]; } for (int i = 0; i < ymaps.Count; i++) { var ymap = ymaps[i]; var name = ymapNames[i]; if (name.StartsWith("portal_") || name.StartsWith("entityset_")) { continue; } var roomIdx = mlo.Rooms.FindIndex(e => e.Name == name); MCMloRoomDef currRoom = null; if (roomIdx != -1) { currRoom = mlo.Rooms[roomIdx]; } for (int j = 0; j < ymap.CMapData.Entities.Count; j++) { var entity = ymap.CMapData.Entities[j]; var idx = mlo.Entities.FindIndex(e => e.Guid == entity.Guid); var room = currRoom; var originalPosition = entity.Position; var originalRotation = entity.Rotation; Console.WriteLine(name + " => " + j + " (" + idx + "|" + mlo.Entities.Count + ") => " + Utils.HashString((MetaName)entity.ArchetypeName)); Utils.World2Mlo(entity, mlo, position, rotation); if (opts.Static && idx == -1) { if ((entity.Flags & 32) == 0) { Console.WriteLine(" Setting static flag (32)"); entity.Flags = entity.Flags | 32; } } entity.LodLevel = Unk_1264241711.LODTYPES_DEPTH_ORPHANHD; if (entity.Guid == 0) { var random = new Random(); do { entity.Guid = (uint)random.Next(1000000, Int32.MaxValue); }while (mlo.Entities.Count(e => e.Guid == entity.Guid) > 0); Console.WriteLine(" Setting random GUID => " + entity.Guid); } if (idx == -1) { idx = mlo.AddEntity(entity); } else { Console.WriteLine(" Found matching GUID => Overriding " + idx); mlo.Entities[idx] = entity; } Console.WriteLine(j + " " + Utils.HashString((MetaName)entity.ArchetypeName)); if (room == null) { room = GetRoomForEntity(mlo, roomExtents, entity); } if (room == null) { entity.Position = originalPosition; entity.Rotation = originalRotation; entity.LodLevel = Unk_1264241711.LODTYPES_DEPTH_HD; missingYmap.CMapData.Entities.Add(entity); missingCount++; continue; } uint id = (uint)idx; if (room.AttachedObjects.IndexOf(id) == -1) { room.AttachedObjects.Add(id); } Console.WriteLine(" Room => " + room.Name); } } if (opts.DeleteMissing) { for (int i = mlo.Entities.Count - 1; i >= 0; i--) { bool found = false; for (int j = 0; j < ymaps.Count; j++) { var ymap = ymaps[j]; if (ymap.CMapData.Entities.FindIndex(e => e.Guid == mlo.Entities[i].Guid) != -1) { found = true; break; } } if (!found) { Console.WriteLine("DELETE " + i); for (int j = 0; j < mlo.Rooms.Count; j++) { for (int k = mlo.Rooms[j].AttachedObjects.Count - 1; k >= 0; k--) { if (mlo.Rooms[j].AttachedObjects[k] == (uint)i) { mlo.Rooms[j].AttachedObjects.RemoveAt(k); } } } } } } var foundEntities = new Dictionary <uint, List <MCEntityDef> >(); for (int i = 0; i < ymaps.Count; i++) { var ymap = ymaps[i]; var name = ymapNames[i]; if (!name.StartsWith("entityset_")) { continue; } string[] split = name.Split('_'); uint nameHash = uint.Parse(split[1]); string roomName = split[2]; if (!foundEntities.TryGetValue(nameHash, out List <MCEntityDef> fEntities)) { fEntities = new List <MCEntityDef>(); } int entitySetIdx = mlo.EntitySets.FindIndex(e => e.Name == nameHash); int roomIdx = mlo.Rooms.FindIndex(e => e.Name == roomName); MCMloEntitySet currEntitySet = null; if (entitySetIdx != -1) { currEntitySet = mlo.EntitySets[entitySetIdx]; } for (int j = 0; j < ymap.CMapData.Entities.Count; j++) { var entity = ymap.CMapData.Entities[j]; var idx = currEntitySet.Entities.FindIndex(e => e.Guid == entity.Guid); var entitySet = currEntitySet; var originalPosition = entity.Position; var originalRotation = entity.Rotation; Console.WriteLine(name + " => " + j + " (" + idx + "|" + currEntitySet.Entities.Count + ") => " + Utils.HashString((MetaName)entity.ArchetypeName)); Utils.World2Mlo(entity, mlo, position, rotation); if (opts.Static && idx == -1) { if ((entity.Flags & 32) == 0) { Console.WriteLine(" Setting static flag (32)"); entity.Flags = entity.Flags | 32; } } entity.LodLevel = Unk_1264241711.LODTYPES_DEPTH_ORPHANHD; if (entity.Guid == 0) { var random = new Random(); do { entity.Guid = (uint)random.Next(1000000, Int32.MaxValue); }while (currEntitySet.Entities.Count(e => e.Guid == entity.Guid) > 0); Console.WriteLine(" Setting random GUID => " + entity.Guid); } if (idx == -1) { idx = currEntitySet.AddEntity(entity, roomIdx); } else { Console.WriteLine(" Found matching GUID => Overriding " + idx); currEntitySet.Entities[idx] = entity; } Console.WriteLine(j + " " + Utils.HashString((MetaName)entity.ArchetypeName)); fEntities.Add(entity); } foundEntities[nameHash] = fEntities; } if (opts.DeleteMissing) { foreach (var entry in foundEntities) { var entitySet = mlo.EntitySets.Find(e => e.Name == entry.Key); for (int i = entitySet.Entities.Count - 1; i >= 0; i--) { bool found = false; if (entry.Value.FindIndex(e => e.Guid == entitySet.Entities[i].Guid) != -1) { found = true; } if (!found) { Console.WriteLine("DELETE " + i); entitySet.RemoveEntity(entitySet.Entities[i]); } } } } ytyp.Save(opts.Name + ".ytyp"); if (missingCount > 0) { var extents = Utils.CalcExtents(missingYmap.CMapData.Entities); missingYmap.CMapData.EntitiesExtentsMin = extents[0][0]; missingYmap.CMapData.EntitiesExtentsMax = extents[0][1]; missingYmap.CMapData.StreamingExtentsMin = extents[1][0]; missingYmap.CMapData.StreamingExtentsMax = extents[1][1]; missingYmap.Save(opts.Name + "_exterior.ymap"); } }); }
static void HandleExtractEntitiesOptions(string[] args) { CommandLine.Parse <ExtractEntitiesOptions>(args, (opts, gOpts) => { if (opts.Ytyp == null) { Console.WriteLine("Please provide source ytyp file with --ytyp"); return; } if (opts.Position == null || opts.Position.Count() != 3) { Console.WriteLine("Please provide a correct position ex: --position 120.5,1370.312,769.2"); return; } if (opts.Rotation == null || opts.Rotation.Count() != 4) { Console.WriteLine("Plase provide a correct rotation ex: --rotation 0,0,0,1"); return; } if (opts.Name == null) { Console.WriteLine("Plase output directory name with --name"); return; } Init(args); var position = new Vector3(opts.Position.ElementAt(0), opts.Position.ElementAt(1), opts.Position.ElementAt(2)); var rotation = new Quaternion(opts.Rotation.ElementAt(0), opts.Rotation.ElementAt(1), opts.Rotation.ElementAt(2), opts.Rotation.ElementAt(3)); var ytyp = new YtypFile(); ytyp.Load(opts.Ytyp); if (!File.Exists(opts.Name + ".original.ytyp")) { File.Copy(opts.Ytyp, opts.Name + ".original.ytyp"); } MCMloArchetypeDef mlo = null; for (int i = 0; i < ytyp.CMapTypes.MloArchetypes.Count; i++) { if (opts.MloName == null) { mlo = ytyp.CMapTypes.MloArchetypes[i]; break; } else { uint mloNameHash = Jenkins.Hash(opts.MloName.ToLowerInvariant()); if (mloNameHash == ytyp.CMapTypes.MloArchetypes[i].Name) { Console.Error.WriteLine("Found MLO => " + opts.MloName); mlo = ytyp.CMapTypes.MloArchetypes[i]; break; } } } if (mlo == null) { Console.WriteLine("MLO archetype not found"); return; } for (int roomId = 0; roomId < mlo.Rooms.Count; roomId++) { var room = mlo.Rooms[roomId]; var ymap = new YmapFile(); var ymapEntities = new List <MCEntityDef>(); Console.WriteLine("Room => " + room.Name + " (" + room.AttachedObjects.Count + " entities)"); for (int i = 0; i < room.AttachedObjects.Count; i++) { int idx = (int)room.AttachedObjects[i]; if (idx >= mlo.Entities.Count) { continue; } var entity = mlo.Entities[idx]; var entityRotation = new Quaternion(entity.Rotation.X, entity.Rotation.Y, entity.Rotation.Z, entity.Rotation.W); Utils.Mlo2World(entity, mlo, position, rotation); entity.LodLevel = Unk_1264241711.LODTYPES_DEPTH_HD; if (entity.Guid == 0) { var random = new Random(); do { entity.Guid = (uint)random.Next(1000000, Int32.MaxValue); }while (mlo.Entities.Count(e => e.Guid == entity.Guid) == 1); Console.WriteLine("[" + i + "] Setting random GUID => " + entity.Guid); } ymapEntities.Add(entity); } ymap.CMapData.Entities = ymapEntities; var extents = Utils.CalcExtents(ymap.CMapData.Entities); ymap.CMapData.EntitiesExtentsMin = extents[0][0]; ymap.CMapData.EntitiesExtentsMax = extents[0][1]; ymap.CMapData.StreamingExtentsMin = extents[1][0]; ymap.CMapData.StreamingExtentsMax = extents[1][1]; Console.WriteLine(extents[0][0].X + " " + extents[0][0].Y + " " + extents[0][0].Z); Console.WriteLine(extents[0][1].X + " " + extents[0][1].Y + " " + extents[0][1].Z); Directory.CreateDirectory(opts.Name); ymap.Save(opts.Name + "\\" + room.Name + ".ymap"); } if (mlo.EntitySets != null) { for (int i = 0; i < mlo.EntitySets.Count; i++) { var entitySet = mlo.EntitySets[i]; Directory.CreateDirectory(opts.Name + "\\entitysets\\" + entitySet.Name); for (int roomId = 0; roomId < mlo.Rooms.Count; roomId++) { var room = mlo.Rooms[roomId]; var ymap = new YmapFile(); var ymapEntities = new List <MCEntityDef>(); Console.WriteLine("EntitySet => " + entitySet.Name + " [" + room.Name + "] (" + entitySet.Entities.Count + " entities)"); for (int j = 0; j < entitySet.Entities.Count; j++) { int targetRoom = (int)entitySet.Locations[j]; if (targetRoom != roomId) { continue; } var entity = entitySet.Entities[j]; var entityRotation = new Quaternion(entity.Rotation.X, entity.Rotation.Y, entity.Rotation.Z, entity.Rotation.W); Utils.Mlo2World(entity, mlo, position, rotation); entity.LodLevel = Unk_1264241711.LODTYPES_DEPTH_HD; if (entity.Guid == 0) { var random = new Random(); do { entity.Guid = (uint)random.Next(1000000, Int32.MaxValue); }while (mlo.Entities.Count(e => e.Guid == entity.Guid) == 1); Console.WriteLine("[" + i + "] Setting random GUID => " + entity.Guid); } ymapEntities.Add(entity); } ymap.CMapData.Entities = ymapEntities; var extents = Utils.CalcExtents(ymap.CMapData.Entities); ymap.CMapData.EntitiesExtentsMin = extents[0][0]; ymap.CMapData.EntitiesExtentsMax = extents[0][1]; ymap.CMapData.StreamingExtentsMin = extents[1][0]; ymap.CMapData.StreamingExtentsMax = extents[1][1]; Console.WriteLine(extents[0][0].X + " " + extents[0][0].Y + " " + extents[0][0].Z); Console.WriteLine(extents[0][1].X + " " + extents[0][1].Y + " " + extents[0][1].Z); ymap.Save(opts.Name + "\\entitysets\\" + entitySet.Name + "\\entityset_" + entitySet.Name + "_" + room.Name + ".ymap"); } } } /* * for(int portalId=0; portalId < mlo.Portals.Count; portalId++) * { * var portal = mlo.Portals[portalId]; * var ymap = new YmapFile(); * var ymapEntities = new List<MCEntityDef>(); * var entitiesExtents = new List<Tuple<Vector3, Vector3>>(); * var streamingExtents = new List<Tuple<Vector3, Vector3>>(); * * Console.WriteLine("Portal => " + portalId + " (" + portal.AttachedObjects.Count + " entities)"); * * for (int i = 0; i < portal.AttachedObjects.Count; i++) * { * int idx = (int)portal.AttachedObjects[i]; * * if (idx >= mlo.Entities.Count) * continue; * * var entity = mlo.Entities[idx]; * var entityRotation = new Quaternion(entity.Rotation.X, entity.Rotation.Y, entity.Rotation.Z, entity.Rotation.W); * * Utils.Mlo2World(entity, mlo, position, rotation); * * entity.LodLevel = Unk_1264241711.LODTYPES_DEPTH_HD; * * if (entity.Guid == 0) * { * var random = new Random(); * * do * { * entity.Guid = (uint)random.Next(1000000, Int32.MaxValue); * } * while (mlo.Entities.Count(e => e.Guid == entity.Guid) == 1); * * Console.WriteLine("[" + i + "] Setting random GUID => " + entity.Guid); * } * * ymapEntities.Add(entity); * } * * ymap.CMapData.Entities = ymapEntities; * * var extents = Utils.CalcExtents(ymap.CMapData.Entities); * * ymap.CMapData.EntitiesExtentsMin = extents[0][0]; * ymap.CMapData.EntitiesExtentsMax = extents[0][1]; * ymap.CMapData.StreamingExtentsMin = extents[1][0]; * ymap.CMapData.StreamingExtentsMax = extents[1][1]; * * Console.WriteLine(extents[0][0].X + " " + extents[0][0].Y + " " + extents[0][0].Z); * Console.WriteLine(extents[0][1].X + " " + extents[0][1].Y + " " + extents[0][1].Z); * * Directory.CreateDirectory(opts.Name); * * ymap.Save(opts.Name + "\\portal_" + portalId.ToString().PadLeft(3, '0') + ".ymap"); * * var data = new JObject() * { * ["corners"] = new JArray() * { * new JObject() { ["x"] = portal.Corners[0][0], ["y"] = portal.Corners[0][1], ["z"] = portal.Corners[0][2] }, * new JObject() { ["x"] = portal.Corners[1][0], ["y"] = portal.Corners[1][1], ["z"] = portal.Corners[1][2] }, * new JObject() { ["x"] = portal.Corners[2][0], ["y"] = portal.Corners[2][1], ["z"] = portal.Corners[2][2] }, * new JObject() { ["x"] = portal.Corners[3][0], ["y"] = portal.Corners[3][1], ["z"] = portal.Corners[3][2] }, * }, * ["flags"] = portal.Flags, * ["mirrorPriority"] = portal.MirrorPriority, * ["opacity"] = portal.Opacity, * ["roomFrom"] = portal.RoomFrom, * ["roomTo"] = portal.RoomTo, * }; * * var jsonString = JsonConvert.SerializeObject(data, new JsonSerializerSettings() { Formatting = Newtonsoft.Json.Formatting.Indented }); * * File.WriteAllText(opts.Name + "\\portal_" + portalId.ToString().PadLeft(3, '0') + ".json", jsonString); * } */ }); }