static void UpdateModelAnimation(FoamModel Mdl) { if (Mdl.Animations != null) { FoamAnimation Anim = Mdl.Animations[0]; int Frames = Anim.Frames.Length; float TicksPerSecond = Anim.TicksPerSecond; if (TicksPerSecond == 0) { TicksPerSecond = 21; } float SecondsPerFrame = (Anim.DurationInTicks / TicksPerSecond) / Frames; if (AnimStopwatch == null) { AnimStopwatch = Stopwatch.StartNew(); } if ((AnimStopwatch.ElapsedMilliseconds / 1000.0f) >= SecondsPerFrame) { FrameIndex++; if (FrameIndex >= Anim.Frames.Length) { FrameIndex = 0; } UpdateModel(Mdl, FrameIndex); AnimStopwatch.Restart(); } } }
static void Convert(string FilePath) { string FileName = Path.GetFileNameWithoutExtension(FilePath); string Extension = Path.GetExtension(FilePath).ToLower(); string RootDir = Path.GetDirectoryName(FilePath); FoamModel Foam = null; switch (Extension) { case ".md3": { string AnimationCfgFile = Path.Combine(RootDir, "animation.cfg"); MD3_AnimationCfg AnimationCfg = new MD3_AnimationCfg(AnimationCfgFile); bool IsHead = FilePath.Contains("head"); bool IsUpper = FilePath.Contains("upper"); bool IsLower = FilePath.Contains("lower"); //Foam = new FoamModel(FileName, FoamFlags.Model, null, null, null, null); Foam = Load(FilePath); // TODO: Manual MD3 loader break; } default: Foam = Load(FilePath); break; } Foam.SaveToFile(Path.Combine(RootDir, FileName + ".foam")); }
static void Convert(string FilePath) { Console.WriteLine("Converting {0}", FilePath); string FileName = Path.GetFileNameWithoutExtension(FilePath); FoamModel Model = LoadMdl(FilePath); if (!Directory.Exists("foam")) { Directory.CreateDirectory("foam"); } Model.SaveToFile("foam/" + FileName + ".foam"); }
static FoamModel[] LoadAllFrames(string MD3File) { List <FoamModel> Frames = new List <FoamModel>(); try { for (int i = 0; i < 1025; i++) { FoamModel Frame = Load(MD3File, i); Frames.Add(Frame); } } catch (AssimpException) { } return(Frames.ToArray()); }
static Model[] LoadModels(string FileName, out float Scale, out FoamModel FoamModel) { string RootDir = Path.GetDirectoryName(FileName); Scale = 0; FoamModel = null; string Ext = Path.GetExtension(FileName).ToLower(); if (!(Ext == ".foam" || Ext == ".mapfoam")) { FoamModel = FoamConverter.Load(FileName); // FoamModel.SaveToFile(Path.Combine(RootDir, Path.GetFileNameWithoutExtension(FileName) + ".foam")); } //try { if (FoamModel == null) { FoamModel = FoamModel.FromFile(FileName); } FoamModel.CalcBounds(out Vector3 Min, out Vector3 Max); Scale = Utils.Max(Max - Min); List <Model> LoadedModels = new List <Model>(); foreach (var M in FoamModel.Meshes) { LoadedModels.Add(FoamMeshToModel(RootDir, M, FoamModel)); } return(LoadedModels.ToArray()); /*} catch (Exception E) { * Console.WriteLine("{0}", E.Message); * }*/ return(null); }
static void UpdateModel(FoamModel Model, int FrameIndex) { if (Model.Animations == null) { return; } //Matrix4x4 ParentRotMat = Matrix4x4.CreateFromYawPitchRoll(0, -Pi / 2, 0); foreach (var Msh in Model.Meshes) { List <Vertex3> Verts = new List <Vertex3>(); if (Msh.BoneInformation == null) { continue; } foreach (var Index in Msh.Indices) { FoamVertex3 Vert = Msh.Vertices[Index]; FoamBoneInfo Info = Msh.BoneInformation[Index]; FoamBone Bone1 = Model.Bones[Info.Bone1]; // TODO: Weights Matrix4x4 BindWorld = Bone1.BindMatrix; Matrix4x4 WorldTrans = Model.CalcWorldTransform(0, FrameIndex, Info.Bone1); Vector3 Pos = Vector3.Transform(Vert.Position, BindWorld * WorldTrans); // TODO: Flip? Verts.Add(new Vertex3(Pos, Vert.UV)); } Mesh *RayMesh = ((Model)Msh.Userdata).meshes; Raylib.UnloadMesh(*RayMesh); *RayMesh = Raylib.GenMeshRaw(Verts.ToArray()); } }
static Model FoamMeshToModel(string RootDir, FoamMesh Mesh, FoamModel FoamModel) { Vertex3[] Verts = Mesh.GetFlatVertices().Select(V => ToVert3(V)).ToArray(); Mesh RaylibMesh = Raylib.GenMeshRaw(Verts); Model Mdl = Raylib.LoadModelFromMesh(RaylibMesh); // Mdl.transform = Matrix4x4.CreateFromYawPitchRoll(0, Pi / 2, 0); Mesh.Userdata = Mdl; foreach (var Ext in FoamModel.Extensions) { if (Ext.Name.Contains("lightmap")) { Texture2D LightmapTex = LoadTexture(Ext); for (int i = 0; i < FoamModel.Materials.Length; i++) { if (FoamModel.Materials[i].FindTexture(FoamTextureType.LightMap, out FoamTexture LightTex)) { SetTexture(Mdl, LightmapTex); } } break; } } if (FoamModel.Materials[Mesh.MaterialIndex].FindTexture(FoamTextureType.Diffuse, out FoamTexture Tex)) { SetTexture(Mdl, Path.Combine(RootDir, Tex.Name)); } //Mdl.transform = Matrix4x4.CreateFromYawPitchRoll(0, Pi / 2, 0); Mdl.transform = Matrix4x4.CreateScale(0.01f); return(Mdl); }
static void Main(string[] args) { Raylib.InitWindow(1366, 768, "Foam Test"); Raylib.SetTargetFPS(60); //args = new string[] { "C:/Projekti/Foam/bin/mapfoam/sample/test.mapfoam" }; //args = new[] { "C:/Projekti/Ray/build/bin/sample/light_test.mapfoam" }; Cam3D = new Camera3D(new Vector3(1, 1, 1), Vector3.Zero, Vector3.UnitY); Raylib.SetCameraMode(Cam3D, CameraMode.CAMERA_FREE); Model[] Models = null; FoamModel FoamModel = null; bool DrawText = true; bool DrawWireframe = false; bool DrawSkeleton = true; bool UpdateAnimation = true; while (!Raylib.WindowShouldClose()) { if (Raylib.IsKeyPressed(KeyboardKey.KEY_F1)) { DrawText = !DrawText; } if (Raylib.IsKeyPressed(KeyboardKey.KEY_F2)) { DrawWireframe = !DrawWireframe; } if (Raylib.IsKeyPressed(KeyboardKey.KEY_F3)) { DrawSkeleton = !DrawSkeleton; } if (Raylib.IsKeyPressed(KeyboardKey.KEY_F4)) { UpdateAnimation = !UpdateAnimation; } if (Raylib.IsFileDropped() || (args != null && args.Length > 0)) { string DroppedFile = null; if (args != null && args.Length > 0) { DroppedFile = args[0]; args = null; } else { DroppedFile = Raylib.GetDroppedFiles()[0]; } Model[] NewModels = LoadModels(DroppedFile, out float Scale, out FoamModel NewFoamModel); if (NewModels != null && NewFoamModel != null) { if (NewModels.Length == 0 && NewFoamModel.Animations != null && FoamModel != null) { foreach (var NewAnim in NewFoamModel.Animations) { Utils.Append(ref FoamModel.Animations, NewAnim); } } else { if (Models != null) { foreach (var M in Models) { Raylib.UnloadModel(M); } } Models = NewModels; FoamModel = NewFoamModel; //Cam3D.position = new Vector3(0.5f, 0.25f, 0.5f) * Scale; //Cam3D.target = new Vector3(0, 0.25f, 0) * Scale; } } Raylib.ClearDroppedFiles(); } /*if (Models != null) * UpdateModel(FoamModel, FrameIndex);*/ Raylib.BeginDrawing(); Raylib.ClearBackground(new Color(50, 50, 50)); //Raylib.ClearBackground(new Color(0, 0, 0)); if (Models != null) { Raylib.UpdateCamera(ref Cam3D); Raylib.BeginMode3D(Cam3D); for (int i = 0; i < Models.Length; i++) { if (DrawWireframe) { Raylib.DrawModelWires(Models[i], Vector3.Zero, 1, Color.White); } else { Raylib.DrawModel(Models[i], Vector3.Zero, 1, Color.White); } } if (UpdateAnimation) { UpdateModelAnimation(FoamModel); } if (FoamModel != null && DrawSkeleton) { DrawBones(FoamModel); } Raylib.EndMode3D(); if (DrawText) { if (WorldTexts != null) { foreach (var KV in WorldTexts) { Raylib.DrawText(KV.Value, (int)KV.Key.X, (int)KV.Key.Y, 10, Color.White); } } } } DrawTextLine("F1 - Toggle bone names", 0); DrawTextLine("F2 - Toggle wireframe", 1); DrawTextLine("F3 - Toggle skeleton", 2); DrawTextLine("F4 - Toggle animations", 3); Raylib.DrawFPS(5, 5); Raylib.EndDrawing(); } }
static void DrawBones(FoamModel Mdl) { if (Mdl.Bones == null) { return; } if (WorldTexts == null || WorldTexts.Length != Mdl.Bones.Length) { WorldTexts = new KeyValuePair <Vector2, string> [Mdl.Bones.Length]; for (int i = 0; i < WorldTexts.Length; i++) { WorldTexts[i] = new KeyValuePair <Vector2, string>(new Vector2(0, 0), "null"); } } //Matrix4x4 ParentRotMat = Matrix4x4.CreateFromYawPitchRoll(0, -Pi / 2, 0); for (int i = 0; i < Mdl.Bones.Length; i++) { FoamBone Bone = Mdl.Bones[i]; //* if (Mdl.Animations != null) { // Actual bones Matrix4x4 ParentWorld = Matrix4x4.Identity; if (Bone.ParentBoneIndex != -1) { ParentWorld = Mdl.CalcWorldTransform(0, FrameIndex, Bone.ParentBoneIndex); } Matrix4x4.Decompose(ParentWorld, out Vector3 ParentScale, out Quaternion ParentRot, out Vector3 ParentPos); Matrix4x4 World = Mdl.CalcWorldTransform(0, FrameIndex, i); Matrix4x4.Decompose(World, out Vector3 Scale, out Quaternion Rot, out Vector3 Pos); DrawLine3D(ParentPos, Pos, Color.Red); // Text Vector3 Center = (Pos + ParentPos) / 2; WorldTexts[i] = new KeyValuePair <Vector2, string>(Raylib.GetWorldToScreen(RotateVec3(Center), Cam3D), Bone.Name); } //*/ // Bind pose bones Matrix4x4 BindParentWorld = Matrix4x4.Identity; if (Bone.ParentBoneIndex != -1) { BindParentWorld = Mdl.CalcBindTransform(Bone.ParentBoneIndex); } Matrix4x4.Decompose(BindParentWorld, out Vector3 BindParentScale, out Quaternion BindParentRot, out Vector3 BindParentPos); Matrix4x4 BindWorld = Mdl.CalcBindTransform(i); Matrix4x4.Decompose(BindWorld, out Vector3 BindScale, out Quaternion BindRot, out Vector3 BindPos); DrawLine3D(BindParentPos, BindPos, Color.Green); //*/ } }
static void Run() { string ObjInput = ArgumentParser.GetSingle("obj"); string MtlInput = ArgumentParser.GetSingle("mtl"); string MapOutput = ArgumentParser.GetSingle("out"); bool EmbedTextures = ArgumentParser.Defined("e"); bool ComputeLights = !ArgumentParser.Defined("l"); string ObjName = "test"; if (ObjInput == null) { ObjInput = "sample/" + ObjName + ".obj"; MtlInput = "sample/" + ObjName + ".mtl"; MapOutput = "sample/" + ObjName + ".mapfoam"; } if (!File.Exists(ObjInput)) { throw new Exception("Obj input file not found"); } if (!File.Exists(MtlInput)) { throw new Exception("Mtl input file not found"); } string OutDir = Path.GetDirectoryName(Path.GetFullPath(MapOutput)); Console.WriteLine("obj = '{0}'", ObjInput); Console.WriteLine("mtl = '{0}'", MtlInput); Console.WriteLine("out = '{0}'", MapOutput); Console.WriteLine("Embed textures? {0}", EmbedTextures); Console.WriteLine("Compute lights? {0}", ComputeLights); FoamModel LevelModel = ObjLoader.Load(ObjInput, MtlInput); MeshAtlasMap AtlasMap; LevelModel.CalcBounds(out Vector3 Min, out Vector3 Max); Console.WriteLine("Level min = {0}; max = {1}", Min, Max); // Generate atlas { Console.WriteLine("Generating lightmap"); GenAtlas(OutDir, LevelModel, out AtlasMap); } if (ComputeLights) { Light[] Lights = new Light[] { new Light(new Vector3(-32, 104, 368), new Vector3(1, 0, 0), 10000), new Light(new Vector3(-80, 104, 704), new Vector3(0, 1, 0), 10000), new Light(new Vector3(0, 312, 304), new Vector3(0, 0, 1), 20000) }; //LightMapping.Compute(LevelModel, AtlasMap, Lights); //Vector4[] Pixels = new Vector4[1024 * 1024]; //RayLightmapper.raylight_render_scene(1024, 1024, 512, new Vector3(1, 1, 1), 0, null, 0, null, null, null, ref Pixels); { string TexName = "lightmap.png"; FoamMaterial LightmapMat = new FoamMaterial(Path.GetFileNameWithoutExtension(TexName), new[] { new FoamTexture(TexName, FoamTextureType.LightMap) }); Utils.Append(ref LevelModel.Materials, LightmapMat); foreach (var M in LevelModel.Meshes) { M.MaterialIndex = LevelModel.Materials.Length - 1; } //AtlasMap.Atlas.Resize(4); AtlasMap.Atlas.FlipY(); AtlasMap.Atlas.Save(Path.Combine(OutDir, TexName)); Utils.Append(ref LevelModel.Extensions, FoamExtension.CreateEmbeddedPng(TexName, AtlasMap.Atlas.GetImage())); } } //ObjLoader.Save(LevelModel, "sample/EXPORTED.obj"); LevelModel.SaveToFile(MapOutput); //Console.WriteLine("Done!"); //Console.ReadLine(); }
static void ApplyEmissive(Vector4[] Pixels, int W, int H, List <FoamVertex3> ModelVerts, List <FoamMesh> Meshes, FoamModel Model) { Vector4 ClrR = new Vector4(1, 0, 0, 1); Vector4 ClrG = new Vector4(0, 1, 0, 1); Vector4 ClrB = new Vector4(0, 0, 1, 1); Vector4 ClrW = new Vector4(1, 1, 1, 1); Vector2 Size = new Vector2(W, H); for (int i = 0; i < ModelVerts.Count; i += 3) { FoamMaterial Mat = Model.Materials[Meshes[i].MaterialIndex]; // TODO: Move somewhere else switch (Mat.MaterialName) { case "base/emissive_red": DrawTriangle(Pixels, W, H, ClrR, ModelVerts[i + 0].UV2 * Size, ModelVerts[i + 1].UV2 * Size, ModelVerts[i + 2].UV2 * Size); break; case "base/emissive_green": DrawTriangle(Pixels, W, H, ClrG, ModelVerts[i + 0].UV2 * Size, ModelVerts[i + 1].UV2 * Size, ModelVerts[i + 2].UV2 * Size); break; case "base/emissive_blue": DrawTriangle(Pixels, W, H, ClrB, ModelVerts[i + 0].UV2 * Size, ModelVerts[i + 1].UV2 * Size, ModelVerts[i + 2].UV2 * Size); break; //case "base/emissive_sky": case "base/emissive_white": DrawTriangle(Pixels, W, H, ClrW, ModelVerts[i + 0].UV2 * Size, ModelVerts[i + 1].UV2 * Size, ModelVerts[i + 2].UV2 * Size); break; default: break; } } }
static void GenAtlas(string OutDir, FoamModel LevelModel, out MeshAtlasMap AtlasMap) { const bool GenerateNewUVs = true; const float TextureScale = 2; //const float VertexScale = 1.0f / 100; const float VertexScale = 1.0f; const int BounceCount = 2; FoamMesh[] Meshes = LevelModel.Meshes; List <FoamMesh> VertexMeshMap = new List <FoamMesh>(); List <FoamVertex3> ModelVerts = new List <FoamVertex3>(); //ushort[] NewInds = null; int W; int H; { foreach (var Mesh in Meshes) { IEnumerable <FoamVertex3> MeshVerts = Mesh.GetFlatVertices(); foreach (var V in MeshVerts) { VertexMeshMap.Add(Mesh); ModelVerts.Add(V); } Mesh.Indices = new ushort[] { }; Mesh.Vertices = new FoamVertex3[] { }; } if (GenerateNewUVs) { AtlasStruct * _Atlas = XAtlas.Create(); XAtlas_MeshDecl MeshDecl = new XAtlas_MeshDecl(ModelVerts.Select(V => V.Position).ToArray()); XAtlas.AddMesh(_Atlas, ref MeshDecl); XAtlas.Generate(_Atlas, ChartOptions.CreateOptions(), null, PackOptions.CreatePackOptions()); XAtlasMesh *XMsh = &_Atlas->meshes[0]; /*NewInds = null; * for (int i = 0; i < XMsh->indexCount; i++) * Utils.Append(ref NewInds, (ushort)XMsh->indexArray[i]);*/ W = (int)(_Atlas->width * TextureScale); H = (int)(_Atlas->height * TextureScale); AtlasMap = new MeshAtlasMap(W, H); FoamVertex3[] OldModelVerts = ModelVerts.ToArray(); ModelVerts.Clear(); FoamMesh[] OldVertexMeshMap = VertexMeshMap.ToArray(); VertexMeshMap.Clear(); Vector2 AtlasSize = new Vector2((int)_Atlas->width, (int)_Atlas->height); for (int i = 0; i < XMsh->vertexCount; i++) { XAtlasVertex *XVert = &XMsh->vertexArray[i]; FoamVertex3 OldVert = OldModelVerts[(int)XVert->xref]; FoamMesh OldMesh = OldVertexMeshMap[(int)XVert->xref]; //OldVert.UV = (XVert->UV) / AtlasSize; OldVert.UV2 = (XVert->UV) / AtlasSize; //OldVert.UV.X = (int)OldVert.UV.X; //OldVert.UV.Y = (int)OldVert.UV.Y; ModelVerts.Add(OldVert); VertexMeshMap.Add(OldMesh); } } else { /*for (int i = 0; i < ModelVerts.Count; i++) * Utils.Append(ref NewInds, (ushort)i);*/ AtlasMap = new MeshAtlasMap(512, 512); } } Vector3[] RL_Pos = new Vector3[ModelVerts.Count]; Vector2[] RL_UV = new Vector2[ModelVerts.Count]; // Calculate normals for (int i = 0; i < ModelVerts.Count; i += 3) { FoamVertex3 VA = ModelVerts[i + 0]; FoamVertex3 VB = ModelVerts[i + 1]; FoamVertex3 VC = ModelVerts[i + 2]; Vector3 CB = VC.Position - VB.Position; Vector3 AB = VA.Position - VB.Position; Vector3 Normal = Vector3.Normalize(Vector3.Cross(CB, AB)); VA.Normal = Normal; VB.Normal = Normal; VC.Normal = Normal; ModelVerts[i + 0] = VA; ModelVerts[i + 1] = VB; ModelVerts[i + 2] = VC; RL_Pos[i + 0] = VA.Position * VertexScale; RL_Pos[i + 1] = VB.Position * VertexScale; RL_Pos[i + 2] = VC.Position * VertexScale; RL_UV[i + 0] = VA.UV2; RL_UV[i + 1] = VB.UV2; RL_UV[i + 2] = VC.UV2; } Vector2 PixelsSize = new Vector2(W, H); Vector4[] Pixels = new Vector4[W * H]; ApplyEmissive(Pixels, W, H, ModelVerts, VertexMeshMap, LevelModel); //int TriIdx = 6; //Vector4 TriClr = new Vector4(50.0f / 255, 100.0f / 255, 200.0f / 255, 1); //DrawTriangle(Pixels, W, H, TriClr, ModelVerts[TriIdx * 3 + 0].UV2 * PixelsSize, ModelVerts[TriIdx * 3 + 1].UV2 * PixelsSize, ModelVerts[TriIdx * 3 + 2].UV2 * PixelsSize); Lightmapper.GenerateLightmap(ref Pixels, W, H, RL_Pos, RL_UV, BounceCount); ApplyEmissive(Pixels, W, H, ModelVerts, VertexMeshMap, LevelModel); //DrawTriangle(Pixels, W, H, TriClr, ModelVerts[TriIdx * 3 + 0].UV2 * PixelsSize, ModelVerts[TriIdx * 3 + 1].UV2 * PixelsSize, ModelVerts[TriIdx * 3 + 2].UV2 * PixelsSize); for (int i = 0; i < Pixels.Length; i++) { int X = i % W; int Y = (i - X) / W; Vector4 Clr = Pixels[i]; Clr = Utils.Clamp(Clr, Vector4.Zero, Vector4.One); AtlasMap.Atlas.SetPixel(X, Y, new FastColor((byte)(Clr.X * 255), (byte)(Clr.Y * 255), (byte)(Clr.Z * 255), 255)); } for (int i = 0; i < VertexMeshMap.Count; i++) { FoamMesh CurMesh = VertexMeshMap[i]; //Utils.Append(ref CurMesh.Indices, (ushort)(NewInds[i])); Utils.Append(ref CurMesh.Vertices, ModelVerts[i]); } for (int i = 0; i < Meshes.Length; i++) { int VertCount = Meshes[i].Vertices.Length; Meshes[i].Indices = new ushort[VertCount]; for (int j = 0; j < VertCount; j++) { Meshes[i].Indices[j] = (ushort)j; } } //Msh.Vertices = Verts.ToArray(); //Msh.Indices = NewInds.ToArray(); }