internal static unsafe void Invoke(MaterialInstanceConstant Instance) { long *p = stackalloc long[] { 0L, 0L }; byte *b = (byte *)p; *((IntPtr *)(b + 0)) = Instance; Main.GetProcessEvent(MaterialEditingLibrary.DefaultObject, UpdateMaterialInstance_ptr, new IntPtr(p));; } }
internal static unsafe void Invoke(MaterialInstanceConstant Instance, MaterialInterface NewParent) { long *p = stackalloc long[] { 0L, 0L, 0L }; byte *b = (byte *)p; *((IntPtr *)(b + 0)) = Instance; *((IntPtr *)(b + 8)) = NewParent; Main.GetProcessEvent(MaterialEditingLibrary.DefaultObject, SetMaterialInstanceParent_ptr, new IntPtr(p));; } }
/// <summary> /// Creates a TexturedPreviewMaterial that renders as close to what the given <see cref="MaterialInstanceConstant"/> looks like as possible. /// </summary> /// <param name="texcache">The texture cache to request textures from.</param> /// <param name="mat">The material that this ModelPreviewMaterial will try to look like.</param> public TexturedPreviewMaterial(PreviewTextureCache texcache, MaterialInstanceConstant mat, PackageCache assetCache, List <PreloadedTextureData> preloadedTextures = null) : base(texcache, mat, assetCache, preloadedTextures) { string matPackage = null; if (mat.Export.Parent != null) { matPackage = mat.Export.Parent.FullPath.ToLower(); } foreach (var textureEntry in mat.Textures) { var texObjectName = textureEntry.FullPath.ToLower(); if ((matPackage == null || texObjectName.StartsWith(matPackage)) && texObjectName.Contains("diff")) { // we have found the diffuse texture! DiffuseTextureFullName = textureEntry.FullPath; Debug.WriteLine("Diffuse texture of new material <" + Properties["Name"] + "> is " + DiffuseTextureFullName); return; } } foreach (var textureEntry in mat.Textures) { var texObjectName = textureEntry.ObjectName.Name.ToLower(); if (texObjectName.Contains("diff") || texObjectName.Contains("tex")) { // we have found the diffuse texture! DiffuseTextureFullName = textureEntry.FullPath; Debug.WriteLine("Diffuse texture of new material <" + Properties["Name"] + "> is " + DiffuseTextureFullName); return; } } foreach (var texparam in mat.Textures) { var texObjectName = texparam.ObjectName.Name.ToLower(); if (texObjectName.Contains("detail")) { // I guess a detail texture is good enough if we didn't return for a diffuse texture earlier... DiffuseTextureFullName = texparam.FullPath; Debug.WriteLine("Diffuse (Detail) texture of new material <" + Properties["Name"] + "> is " + DiffuseTextureFullName); return; } } foreach (var texparam in mat.Textures) { var texObjectName = texparam.ObjectName.Name.ToLower(); if (!texObjectName.Contains("norm") && !texObjectName.Contains("opac")) { //Anything is better than nothing I suppose DiffuseTextureFullName = texparam.FullPath; Debug.WriteLine("Using first found texture (last resort) of new material <" + Properties["Name"] + "> as diffuse: " + DiffuseTextureFullName); return; } } }
internal static unsafe LinearColor Invoke(MaterialInstanceConstant Instance, Name ParameterName) { long *p = stackalloc long[] { 0L, 0L, 0L, 0L, 0L, 0L }; byte *b = (byte *)p; *((IntPtr *)(b + 0)) = Instance; *((Name *)(b + 8)) = ParameterName; Main.GetProcessEvent(MaterialEditingLibrary.DefaultObject, GetMaterialInstanceVectorParameterValue_ptr, new IntPtr(p));; return(*((LinearColor *)(b + 20))); } }
internal static unsafe bool Invoke(MaterialInstanceConstant Instance, Name ParameterName, Texture Value) { long *p = stackalloc long[] { 0L, 0L, 0L, 0L, 0L, 0L }; byte *b = (byte *)p; *((IntPtr *)(b + 0)) = Instance; *((Name *)(b + 8)) = ParameterName; *((IntPtr *)(b + 24)) = Value; Main.GetProcessEvent(MaterialEditingLibrary.DefaultObject, SetMaterialInstanceTextureParameterValue_ptr, new IntPtr(p));; return(*((bool *)(b + 32))); } }
/// <summary> /// Creates a ModelPreviewMaterial that renders as close to what the given <see cref="MaterialInstanceConstant"/> looks like as possible. /// </summary> /// <param name="texcache">The texture cache to request textures from.</param> /// <param name="mat">The material that this ModelPreviewMaterial will try to look like.</param> protected ModelPreviewMaterial(PreviewTextureCache texcache, MaterialInstanceConstant mat, PackageCache assetCache, List <PreloadedTextureData> preloadedTextures = null) { if (mat == null) { return; } Properties.Add("Name", mat.Export.ObjectName); foreach (var textureEntry in mat.Textures) { if (!Textures.ContainsKey(textureEntry.FullPath) && textureEntry.ClassName == "Texture2D") //Apparently some assets are cubemaps, we don't want these. { if (preloadedTextures != null) { var preloadedInfo = preloadedTextures.FirstOrDefault(x => x.MaterialExport == mat.Export && x.Mip.Export.ObjectName.Name == textureEntry.ObjectName.Name); //i don't like matching on object name but its export vs import here. if (preloadedInfo != null) { Textures.Add(textureEntry.FullPath, texcache.LoadTexture(preloadedInfo.Mip.Export, preloadedInfo.Mip, preloadedInfo.decompressedTextureData)); } else { Debug.WriteLine("Preloading error"); } //if (textureEntry is ExportEntry texPort && preloadedMipInfo.Export != texPort) throw new Exception(); continue; //Don't further parse } if (textureEntry is ImportEntry import) { var extAsset = EntryImporter.ResolveImport(import, null, assetCache); if (extAsset != null) { Textures.Add(textureEntry.FullPath, texcache.LoadTexture(extAsset)); } } else { Textures.Add(textureEntry.FullPath, texcache.LoadTexture(textureEntry as ExportEntry)); } } } }
/// <summary> /// Creates a preview of the given <see cref="SkeletalMesh"/>. /// </summary> /// <param name="Device">The Direct3D device to use for buffer creation.</param> /// <param name="m">The mesh to generate a preview for.</param> /// <param name="texcache">The texture cache for loading textures.</param> public ModelPreview(Device Device, SkeletalMesh m, PreviewTextureCache texcache, PackageCache assetCache, PreloadedModelData preloadedData = null) { // STEP 1: MATERIALS if (preloadedData == null) { for (int i = 0; i < m.Materials.Length; i++) { UIndex materialUIndex = m.Materials[i]; MaterialInstanceConstant mat = null; if (materialUIndex.value > 0) { mat = new MaterialInstanceConstant(m.Export.FileRef.GetUExport(materialUIndex.value)); } else if (materialUIndex.value < 0) { // The material instance is an import! ImportEntry matImport = m.Export.FileRef.GetImport(materialUIndex.value); var externalAsset = EntryImporter.ResolveImport(matImport, null, assetCache); if (externalAsset != null) { mat = new MaterialInstanceConstant(externalAsset); } } if (mat != null) { ModelPreviewMaterial material; // TODO: pick what material class best fits based on what properties the // MaterialInstanceConstant mat has. // For now, just use the default material. material = new TexturedPreviewMaterial(texcache, mat, assetCache); AddMaterial(material.Properties["Name"], material); } } } else { //Preloaded //sections = preloadedData.sections; var uniqueMaterials = preloadedData.texturePreviewMaterials.Select(x => x.MaterialExport).Distinct(); foreach (var mat in uniqueMaterials) { var material = new TexturedPreviewMaterial(texcache, new MaterialInstanceConstant(mat), assetCache, preloadedData.texturePreviewMaterials); AddMaterial(mat.ObjectName.Name, material); } } // STEP 2: LODS foreach (var lodmodel in m.LODModels) { // Vertices List <WorldVertex> vertices = new List <WorldVertex>(m.Export.Game == MEGame.ME1 ? lodmodel.ME1VertexBufferGPUSkin.Length : lodmodel.VertexBufferGPUSkin.VertexData.Length); if (m.Export.Game == MEGame.ME1) { foreach (var vertex in lodmodel.ME1VertexBufferGPUSkin) { vertices.Add(new WorldVertex(new Vector3(-vertex.Position.X, vertex.Position.Z, vertex.Position.Y), Vector3.Zero, new Vector2(vertex.UV.X, vertex.UV.Y))); } } else { foreach (var vertex in lodmodel.VertexBufferGPUSkin.VertexData) { vertices.Add(new WorldVertex(new Vector3(-vertex.Position.X, vertex.Position.Z, vertex.Position.Y), Vector3.Zero, new Vector2(vertex.UV.X, vertex.UV.Y))); } } // Triangles List <Triangle> triangles = new List <Triangle>(lodmodel.IndexBuffer.Length / 3); for (int i = 0; i < lodmodel.IndexBuffer.Length; i += 3) { triangles.Add(new Triangle(lodmodel.IndexBuffer[i], lodmodel.IndexBuffer[i + 1], lodmodel.IndexBuffer[i + 2])); } WorldMesh mesh = new WorldMesh(Device, triangles, vertices); // Sections List <ModelPreviewSection> sections = new List <ModelPreviewSection>(); foreach (var section in lodmodel.Sections) { if (section.MaterialIndex < Materials.Count) { sections.Add(new ModelPreviewSection(Materials.Keys.ElementAt(section.MaterialIndex), section.BaseIndex, (uint)section.NumTriangles)); } } LODs.Add(new ModelPreviewLOD(mesh, sections)); } }
///<summary>Get the current vector parameter value from a Material Instance</summary> public static LinearColor GetMaterialInstanceVectorParameterValue(MaterialInstanceConstant Instance, Name ParameterName) => MaterialEditingLibrary_methods.GetMaterialInstanceVectorParameterValue_method.Invoke(Instance, ParameterName);
///<summary>Get the current texture parameter value from a Material Instance</summary> public static Texture GetMaterialInstanceTextureParameterValue(MaterialInstanceConstant Instance, Name ParameterName) => MaterialEditingLibrary_methods.GetMaterialInstanceTextureParameterValue_method.Invoke(Instance, ParameterName);
///<summary>Get the current scalar (float) parameter value from a Material Instance</summary> public static float GetMaterialInstanceScalarParameterValue(MaterialInstanceConstant Instance, Name ParameterName) => MaterialEditingLibrary_methods.GetMaterialInstanceScalarParameterValue_method.Invoke(Instance, ParameterName);
///<summary>Clears all material parameters set by this Material Instance</summary> public static void ClearAllMaterialInstanceParameters(MaterialInstanceConstant Instance) => MaterialEditingLibrary_methods.ClearAllMaterialInstanceParameters_method.Invoke(Instance);
///<summary>Called after making modifications to a Material Instance to recompile shaders etc.</summary> public static void UpdateMaterialInstance(MaterialInstanceConstant Instance) => MaterialEditingLibrary_methods.UpdateMaterialInstance_method.Invoke(Instance);
///<summary>Set the parent Material or Material Instance to use for this Material Instance</summary> public static void SetMaterialInstanceParent(MaterialInstanceConstant Instance, MaterialInterface NewParent) => MaterialEditingLibrary_methods.SetMaterialInstanceParent_method.Invoke(Instance, NewParent);
/// <summary> /// Creates a preview of the given <see cref="Unreal.Classes.SkeletalMesh"/>. /// </summary> /// <param name="Device">The Direct3D device to use for buffer creation.</param> /// <param name="m">The mesh to generate a preview for.</param> /// <param name="texcache">The texture cache for loading textures.</param> public ModelPreview(Device Device, Unreal.Classes.SkeletalMesh m, PreviewTextureCache texcache) { // STEP 1: MATERIALS for (int i = 0; i < m.Materials.Count; i++) { Unreal.Classes.MaterialInstanceConstant mat = m.MatInsts[i]; if (mat == null && m.Materials[i] < 0) { // The material instance is an import! ImportEntry matImport = m.Export.FileRef.GetImport(m.Materials[i]); var externalAsset = FindExternalAsset(matImport, texcache.cache.Select(x => x.TextureExport).ToList()); if (externalAsset != null) { mat = new MaterialInstanceConstant(externalAsset); } } if (mat != null) { ModelPreviewMaterial material; // TODO: pick what material class best fits based on what properties the // MaterialInstanceConstant mat has. // For now, just use the default material. material = new TexturedPreviewMaterial(texcache, mat); AddMaterial(material.Properties["Name"], material); } } // STEP 2: LODS foreach (Unreal.Classes.SkeletalMesh.LODModelStruct lodmodel in m.LODModels) { // Vertices List <WorldVertex> vertices = new List <WorldVertex>(); if (m.Export.Game == MEGame.ME1) { foreach (Unreal.Classes.SkeletalMesh.GPUSkinVertexStruct vertex in lodmodel.VertexBufferGPUSkin.Vertices) { vertices.Add(new WorldVertex(new Vector3(-vertex.Position.X, vertex.Position.Z, vertex.Position.Y), Vector3.Zero, new Vector2(vertex.UFullPrecision, vertex.VFullPrecision))); } } else { foreach (Unreal.Classes.SkeletalMesh.GPUSkinVertexStruct vertex in lodmodel.VertexBufferGPUSkin.Vertices) { // NOTE: note the switched Y and Z coordinates. Unreal seems to think that Z is up. vertices.Add(new WorldVertex(new Vector3(-vertex.Position.X, vertex.Position.Z, vertex.Position.Y), Vector3.Zero, new Vector2(HalfToFloat(vertex.U), HalfToFloat(vertex.V)))); } } // Triangles List <Triangle> triangles = new List <Triangle>(); for (int i = 0; i < lodmodel.IndexBuffer.Indexes.Count; i += 3) { triangles.Add(new Triangle(lodmodel.IndexBuffer.Indexes[i], lodmodel.IndexBuffer.Indexes[i + 1], lodmodel.IndexBuffer.Indexes[i + 2])); } WorldMesh mesh = new WorldMesh(Device, triangles, vertices); // Sections List <ModelPreviewSection> sections = new List <ModelPreviewSection>(); foreach (Unreal.Classes.SkeletalMesh.SectionStruct section in lodmodel.Sections) { if (section.MaterialIndex < Materials.Count) { sections.Add(new ModelPreviewSection(Materials.Keys.ElementAt(section.MaterialIndex), (uint)section.BaseIndex, (uint)section.NumTriangles)); } } LODs.Add(new ModelPreviewLOD(mesh, sections)); } }
public static string GenerateUDKFileForLevel(string udkPath, IMEPackage pcc) { #region AssetPackage string meshPackageName = $"{Path.GetFileNameWithoutExtension(pcc.FilePath)}Meshes"; string meshFile = Path.Combine(udkPath, @"UDKGame\Content\Shared\", $"{meshPackageName}.upk"); MEPackageHandler.CreateAndSavePackage(meshFile, MEGame.UDK); using IMEPackage meshPackage = MEPackageHandler.OpenUDKPackage(meshFile); meshPackage.getEntryOrAddImport("Core.Package"); IEntry defMat = meshPackage.getEntryOrAddImport("EngineMaterials.DefaultMaterial", "Material", "Engine"); var allMats = new HashSet <int>(); var relinkMap = new Dictionary <IEntry, IEntry>(); #region StaticMeshes List <ExportEntry> staticMeshes = pcc.Exports.Where(exp => exp.ClassName == "StaticMesh").ToList(); foreach (ExportEntry mesh in staticMeshes) { var mats = new Queue <int>(); StaticMesh stm = ObjectBinary.From <StaticMesh>(mesh); foreach (StaticMeshRenderData lodModel in stm.LODModels) { foreach (StaticMeshElement meshElement in lodModel.Elements) { mats.Enqueue(meshElement.Material); allMats.Add(meshElement.Material); meshElement.Material = 0; } } if (pcc.GetEntry(stm.BodySetup) is ExportEntry rbBodySetup) { rbBodySetup.RemoveProperty("PhysMaterial"); } mesh.WriteBinary(stm); IEntry newParent = EntryImporter.GetOrAddCrossImportOrPackage(mesh.ParentFullPath, pcc, meshPackage); EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, mesh, meshPackage, newParent, false, out IEntry ent, relinkMap); ExportEntry portedMesh = (ExportEntry)ent; stm = ObjectBinary.From <StaticMesh>(portedMesh); foreach (StaticMeshRenderData lodModel in stm.LODModels) { foreach (StaticMeshElement meshElement in lodModel.Elements) { meshElement.Material = mats.Dequeue(); } } portedMesh.WriteBinary(stm); } #endregion #region Materials using (IMEPackage udkResources = MEPackageHandler.OpenMEPackageFromStream(Utilities.GetCustomAppResourceStream(MEGame.UDK))) { ExportEntry normDiffMat = udkResources.Exports.First(exp => exp.ObjectName == "NormDiffMat"); foreach (int matUIndex in allMats) { if (pcc.GetEntry(matUIndex) is ExportEntry matExp) { List <IEntry> textures = new MaterialInstanceConstant(matExp).Textures; ExportEntry diff = null; ExportEntry norm = null; foreach (IEntry texEntry in textures) { if (texEntry is ExportEntry texport) { if (texport.ObjectName.Name.ToLower().Contains("diff")) { diff = texport; } else if (texport.ObjectName.Name.ToLower().Contains("norm")) { norm = texport; } } } if (diff == null) { relinkMap[matExp] = defMat; continue; } else { EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, diff, meshPackage, null, false, out IEntry ent); diff = (ExportEntry)ent; diff.RemoveProperty("TextureFileCacheName"); diff.RemoveProperty("TFCFileGuid"); diff.RemoveProperty("LODGroup"); } if (norm != null) { EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, norm, meshPackage, null, false, out IEntry ent); norm = (ExportEntry)ent; norm.RemoveProperty("TextureFileCacheName"); norm.RemoveProperty("TFCFileGuid"); norm.RemoveProperty("LODGroup"); } EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, normDiffMat, meshPackage, null, true, out IEntry matEnt); ExportEntry newMat = (ExportEntry)matEnt; newMat.ObjectName = matExp.ObjectName; Material matBin = ObjectBinary.From <Material>(newMat); matBin.SM3MaterialResource.UniformExpressionTextures = new UIndex[] { norm?.UIndex ?? 0, diff.UIndex }; newMat.WriteBinary(matBin); relinkMap[matExp] = newMat; if (newMat.GetProperty <ArrayProperty <ObjectProperty> >("Expressions") is {} expressionsProp&& expressionsProp.Count >= 2) { ExportEntry diffExpression = meshPackage.GetUExport(expressionsProp[0].Value); ExportEntry normExpression = meshPackage.GetUExport(expressionsProp[1].Value); diffExpression.WriteProperty(new ObjectProperty(diff.UIndex, "Texture")); normExpression.WriteProperty(new ObjectProperty(norm?.UIndex ?? 0, "Texture")); } } else if (pcc.GetEntry(matUIndex) is ImportEntry matImp) { relinkMap[matImp] = defMat; } } var relinkMapping = new OrderedMultiValueDictionary <IEntry, IEntry>(relinkMap); foreach (ExportEntry stmExport in staticMeshes) { if (relinkMap.TryGetValue(stmExport, out IEntry destEnt) && destEnt is ExportEntry destExp) { Relinker.Relink(stmExport, destExp, relinkMapping); } } } #endregion meshPackage.Save(); #endregion var staticMeshActors = new List <ExportEntry>(); var lightActors = new List <ExportEntry>(); string tempPackagePath = Path.Combine(App.ExecFolder, $"{Path.GetFileNameWithoutExtension(pcc.FilePath)}.udk"); File.Copy(Path.Combine(App.ExecFolder, "empty.udk"), tempPackagePath, true); using IMEPackage udkPackage = MEPackageHandler.OpenUDKPackage(tempPackagePath); { var topLevelMeshPackages = new List <IEntry>(); foreach (ExportEntry exportEntry in staticMeshes) { IEntry imp = udkPackage.getEntryOrAddImport($"{exportEntry.FullPath}", "StaticMesh", "Engine", exportEntry.ObjectName.Number); while (imp.Parent != null) { imp = imp.Parent; } if (!topLevelMeshPackages.Contains(imp)) { topLevelMeshPackages.Add(imp); } } ExportEntry levelExport = udkPackage.Exports.First(exp => exp.ClassName == "Level"); List <int> actorsInLevel = ObjectBinary.From <Level>(pcc.Exports.First(exp => exp.ClassName == "Level")).Actors.Select(u => u.value).ToList(); var componentToMatrixMap = new Dictionary <int, Matrix>(); foreach (int uIndex in actorsInLevel) { if (pcc.GetEntry(uIndex) is ExportEntry stcExp) { if (stcExp.ClassName == "StaticMeshCollectionActor") { StaticMeshCollectionActor stmc = ObjectBinary.From <StaticMeshCollectionActor>(stcExp); var components = stcExp.GetProperty <ArrayProperty <ObjectProperty> >("StaticMeshComponents"); for (int i = 0; i < components.Count; i++) { componentToMatrixMap[components[i].Value] = stmc.LocalToWorldTransforms[i]; } } else if (stcExp.ClassName == "StaticLightCollectionActor") { StaticLightCollectionActor stlc = ObjectBinary.From <StaticLightCollectionActor>(stcExp); var components = stcExp.GetProperty <ArrayProperty <ObjectProperty> >("LightComponents"); for (int i = 0; i < components.Count; i++) { componentToMatrixMap[components[i].Value] = stlc.LocalToWorldTransforms[i]; } } } } #region StaticMeshActors { var emptySMCBin = new StaticMeshComponent(); IEntry staticMeshActorClass = udkPackage.getEntryOrAddImport("Engine.StaticMeshActor"); udkPackage.getEntryOrAddImport("Engine.Default__StaticMeshActor", "StaticMeshActor", "Engine"); IEntry staticMeshComponentArchetype = udkPackage.getEntryOrAddImport("Engine.Default__StaticMeshActor.StaticMeshComponent0", "StaticMeshComponent", "Engine"); int smaIndex = 2; int smcIndex = 2; foreach (ExportEntry smc in pcc.Exports.Where(exp => exp.ClassName == "StaticMeshComponent")) { if (smc.Parent is ExportEntry parent && actorsInLevel.Contains(parent.UIndex) && parent.IsA("StaticMeshActorBase")) { StructProperty locationProp; StructProperty rotationProp; StructProperty scaleProp = null; smc.CondenseArchetypes(); if (!(smc.GetProperty <ObjectProperty>("StaticMesh") is { } meshProp) || !pcc.IsUExport(meshProp.Value)) { continue; } smc.WriteBinary(emptySMCBin); smc.RemoveProperty("bBioIsReceivingDecals"); smc.RemoveProperty("bBioForcePrecomputedShadows"); //smc.RemoveProperty("bUsePreComputedShadows"); smc.RemoveProperty("bAcceptsLights"); smc.RemoveProperty("IrrelevantLights"); smc.RemoveProperty("Materials"); //should make use of this? smc.ObjectName = new NameReference("StaticMeshComponent", smcIndex++); if (parent.ClassName == "StaticMeshCollectionActor") { if (!componentToMatrixMap.TryGetValue(smc.UIndex, out Matrix m)) { continue; } (Vector3 posVec, Vector3 scaleVec, Rotator rotator) = m.UnrealDecompose(); locationProp = CommonStructs.Vector3Prop(posVec, "Location"); rotationProp = CommonStructs.RotatorProp(rotator, "Rotation"); scaleProp = CommonStructs.Vector3Prop(scaleVec, "DrawScale3D"); //smc.WriteProperty(CommonStructs.Matrix(m, "CachedParentToWorld")); } else { locationProp = parent.GetProperty <StructProperty>("Location"); rotationProp = parent.GetProperty <StructProperty>("Rotation"); scaleProp = parent.GetProperty <StructProperty>("DrawScale3D"); if (parent.GetProperty <FloatProperty>("DrawScale")?.Value is float scale) { Vector3 scaleVec = Vector3.One; if (scaleProp != null) { scaleVec = CommonStructs.GetVector3(scaleProp); } scaleProp = CommonStructs.Vector3Prop(scaleVec * scale, "DrawScale3D"); } } ExportEntry sma = new ExportEntry(udkPackage, EntryImporter.CreateStack(MEGame.UDK, staticMeshActorClass.UIndex)) { ObjectName = new NameReference("StaticMeshActor", smaIndex++), Class = staticMeshActorClass, Parent = levelExport }; sma.ObjectFlags |= UnrealFlags.EObjectFlags.HasStack; udkPackage.AddExport(sma); EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, smc, udkPackage, sma, true, out IEntry result); var props = new PropertyCollection { new ObjectProperty(result.UIndex, "StaticMeshComponent"), new NameProperty(new NameReference(Path.GetFileNameWithoutExtension(smc.FileRef.FilePath), smc.UIndex), "Tag"), new ObjectProperty(result.UIndex, "CollisionComponent") }; if (locationProp != null) { props.Add(locationProp); } if (rotationProp != null) { props.Add(rotationProp); } if (scaleProp != null) { props.Add(scaleProp); } sma.WriteProperties(props); staticMeshActors.Add(sma); } } IEntry topMeshPackageImport = udkPackage.getEntryOrAddImport(meshPackageName, "Package"); foreach (IEntry mp in topLevelMeshPackages) { mp.Parent = topMeshPackageImport; } } #endregion #region LightActors { IEntry pointLightClass = udkPackage.getEntryOrAddImport("Engine.PointLight"); IEntry spotLightClass = udkPackage.getEntryOrAddImport("Engine.SpotLight"); IEntry directionalLightClass = udkPackage.getEntryOrAddImport("Engine.DirectionalLight"); int plaIndex = 1; int plcIndex = 1; int slaIndex = 1; int slcIndex = 1; int dlaIndex = 1; int dlcIndex = 1; foreach (ExportEntry lightComponent in pcc.Exports) { if (!(lightComponent.Parent is ExportEntry parent && actorsInLevel.Contains(parent.UIndex))) { continue; } StructProperty locationProp; StructProperty rotationProp; StructProperty scaleProp; switch (lightComponent.ClassName) { case "PointLightComponent": lightComponent.CondenseArchetypes(); lightComponent.ObjectName = new NameReference("PointLightComponent", plcIndex++); if (parent.ClassName == "StaticLightCollectionActor") { if (!componentToMatrixMap.TryGetValue(lightComponent.UIndex, out Matrix m)) { continue; } (Vector3 posVec, Vector3 scaleVec, Rotator rotator) = m.UnrealDecompose(); locationProp = CommonStructs.Vector3Prop(posVec, "Location"); rotationProp = CommonStructs.RotatorProp(rotator, "Rotation"); scaleProp = CommonStructs.Vector3Prop(scaleVec, "DrawScale3D"); } else { locationProp = parent.GetProperty <StructProperty>("Location"); rotationProp = parent.GetProperty <StructProperty>("Rotation"); scaleProp = parent.GetProperty <StructProperty>("DrawScale3D"); if (parent.GetProperty <FloatProperty>("DrawScale")?.Value is float scale) { Vector3 scaleVec = Vector3.One; if (scaleProp != null) { scaleVec = CommonStructs.GetVector3(scaleProp); } scaleProp = CommonStructs.Vector3Prop(scaleVec * scale, "DrawScale3D"); } } ExportEntry pla = new ExportEntry(udkPackage, EntryImporter.CreateStack(MEGame.UDK, pointLightClass.UIndex)) { ObjectName = new NameReference("PointLight", plaIndex++), Class = pointLightClass, Parent = levelExport }; pla.ObjectFlags |= UnrealFlags.EObjectFlags.HasStack; udkPackage.AddExport(pla); EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, lightComponent, udkPackage, pla, true, out IEntry portedPLC); var plsProps = new PropertyCollection { new ObjectProperty(portedPLC.UIndex, "LightComponent"), new NameProperty("PointLight", "Tag"), }; if (locationProp != null) { plsProps.Add(locationProp); } if (rotationProp != null) { plsProps.Add(rotationProp); } if (scaleProp != null) { plsProps.Add(scaleProp); } pla.WriteProperties(plsProps); lightActors.Add(pla); break; case "SpotLightComponent": lightComponent.CondenseArchetypes(); lightComponent.ObjectName = new NameReference("SpotLightComponent", slcIndex++); if (parent.ClassName == "StaticLightCollectionActor") { if (!componentToMatrixMap.TryGetValue(lightComponent.UIndex, out Matrix m)) { continue; } (Vector3 posVec, Vector3 scaleVec, Rotator rotator) = m.UnrealDecompose(); locationProp = CommonStructs.Vector3Prop(posVec, "Location"); rotationProp = CommonStructs.RotatorProp(rotator, "Rotation"); scaleProp = CommonStructs.Vector3Prop(scaleVec, "DrawScale3D"); } else { locationProp = parent.GetProperty <StructProperty>("Location"); rotationProp = parent.GetProperty <StructProperty>("Rotation"); scaleProp = parent.GetProperty <StructProperty>("DrawScale3D"); if (parent.GetProperty <FloatProperty>("DrawScale")?.Value is float scale) { Vector3 scaleVec = Vector3.One; if (scaleProp != null) { scaleVec = CommonStructs.GetVector3(scaleProp); } scaleProp = CommonStructs.Vector3Prop(scaleVec * scale, "DrawScale3D"); } } ExportEntry sla = new ExportEntry(udkPackage, EntryImporter.CreateStack(MEGame.UDK, spotLightClass.UIndex)) { ObjectName = new NameReference("SpotLight", slaIndex++), Class = spotLightClass, Parent = levelExport }; sla.ObjectFlags |= UnrealFlags.EObjectFlags.HasStack; udkPackage.AddExport(sla); EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, lightComponent, udkPackage, sla, true, out IEntry portedSLC); var slaProps = new PropertyCollection { new ObjectProperty(portedSLC.UIndex, "LightComponent"), new NameProperty("SpotLight", "Tag"), }; if (locationProp != null) { slaProps.Add(locationProp); } if (rotationProp != null) { slaProps.Add(rotationProp); } if (scaleProp != null) { slaProps.Add(scaleProp); } sla.WriteProperties(slaProps); lightActors.Add(sla); break; case "DirectionalLightComponent": lightComponent.CondenseArchetypes(); lightComponent.ObjectName = new NameReference("DirectionalLightComponent", dlcIndex++); if (parent.ClassName == "StaticLightCollectionActor") { if (!componentToMatrixMap.TryGetValue(lightComponent.UIndex, out Matrix m)) { continue; } (Vector3 posVec, Vector3 scaleVec, Rotator rotator) = m.UnrealDecompose(); locationProp = CommonStructs.Vector3Prop(posVec, "Location"); rotationProp = CommonStructs.RotatorProp(rotator, "Rotation"); scaleProp = CommonStructs.Vector3Prop(scaleVec, "DrawScale3D"); } else { locationProp = parent.GetProperty <StructProperty>("Location"); rotationProp = parent.GetProperty <StructProperty>("Rotation"); scaleProp = parent.GetProperty <StructProperty>("DrawScale3D"); if (parent.GetProperty <FloatProperty>("DrawScale")?.Value is float scale) { Vector3 scaleVec = Vector3.One; if (scaleProp != null) { scaleVec = CommonStructs.GetVector3(scaleProp); } scaleProp = CommonStructs.Vector3Prop(scaleVec * scale, "DrawScale3D"); } } ExportEntry dla = new ExportEntry(udkPackage, EntryImporter.CreateStack(MEGame.UDK, directionalLightClass.UIndex)) { ObjectName = new NameReference("DirectionalLight", dlaIndex++), Class = directionalLightClass, Parent = levelExport }; dla.ObjectFlags |= UnrealFlags.EObjectFlags.HasStack; udkPackage.AddExport(dla); EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, lightComponent, udkPackage, dla, true, out IEntry portedDLC); var dlaProps = new PropertyCollection { new ObjectProperty(portedDLC.UIndex, "LightComponent"), new NameProperty("DirectionalLight", "Tag"), }; if (locationProp != null) { dlaProps.Add(locationProp); } if (rotationProp != null) { dlaProps.Add(rotationProp); } if (scaleProp != null) { dlaProps.Add(scaleProp); } dla.WriteProperties(dlaProps); lightActors.Add(dla); break; } } } UDKifyLights(udkPackage); #endregion Level level = ObjectBinary.From <Level>(levelExport); level.Actors = levelExport.GetChildren().Where(ent => ent.IsA("Actor")).Select(ent => new UIndex(ent.UIndex)).ToList(); levelExport.WriteBinary(level); udkPackage.Save(); } string resultFilePath = Path.Combine(udkPath, @"UDKGame\Content\Maps\", $"{Path.GetFileNameWithoutExtension(pcc.FilePath)}.udk"); using (IMEPackage udkPackage2 = MEPackageHandler.OpenUDKPackage(Path.Combine(App.ExecFolder, "empty.udk"))) { ExportEntry levelExport = udkPackage2.Exports.First(exp => exp.ClassName == "Level"); Level levelBin = ObjectBinary.From <Level>(levelExport); foreach (ExportEntry actor in staticMeshActors) { EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, actor, udkPackage2, levelExport, true, out IEntry result); levelBin.Actors.Add(result.UIndex); } foreach (ExportEntry actor in lightActors) { EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, actor, udkPackage2, levelExport, true, out IEntry result); levelBin.Actors.Add(result.UIndex); } levelExport.WriteBinary(levelBin); udkPackage2.Save(resultFilePath); } File.Delete(tempPackagePath); return(resultFilePath); }