//JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes: //ORIGINAL LINE: @Before public void setUp() public virtual void setUp() { modelInstance = cloneModelInstance(); ModelImpl modelImpl = (ModelImpl)modelInstance.Model; modelImpl.declareAlternativeNamespace(MECHANICAL_NS, TestModelConstants.NEWER_NAMESPACE); }
public IModel LoadModel(BmdModelFileBundle modelFileBundle) { var logger = Logging.Create <BmdModelLoader>(); var bmd = new BMD(modelFileBundle.BmdFile.Impl.ReadAllBytes()); List <(string, IBcx)>?pathsAndBcxs; try { pathsAndBcxs = modelFileBundle .BcxFiles? .Select(bcxFile => { var extension = bcxFile.Extension.ToLower(); IBcx bcx = extension switch { ".bca" => new BCA(bcxFile.Impl.ReadAllBytes()), ".bck" => new BCK(bcxFile.Impl.ReadAllBytes()), _ => throw new NotSupportedException(), }; return(bcxFile.FullName, bcx); }) .ToList(); } catch { logger.LogError("Failed to load BCX!"); throw; } List <(string, BTI)>?pathsAndBtis; try { pathsAndBtis = modelFileBundle .BtiFiles? .Select(btiFile => (btiFile.FullName, new BTI(btiFile.Impl.ReadAllBytes()))) .ToList(); } catch { logger.LogError("Failed to load BTI!"); throw; } var model = new ModelImpl(); var materialManager = new BmdMaterialManager(model, bmd, pathsAndBtis); var jointsAndBones = this.ConvertBones_(model, bmd); this.ConvertAnimations_(model, bmd, pathsAndBcxs, modelFileBundle.FrameRate, jointsAndBones); this.ConvertMesh_(model, bmd, jointsAndBones, materialManager); return(model); }
public TestRadioModule() : base("/test") { Get["/"] = _ => { return(View["Views/testView"]); }; Post["/"] = parameters => { _model = this.Bind <ModelImpl>(); dynamic data = new ExpandoObject(); data.Result = _model.Inputs001; return(View["Views/result", data]); }; }
public IModel LoadModel(DatModelFileBundle modelFileBundle) { var dat = modelFileBundle.DatFile.Impl.ReadNew <Dat>(Endianness.BigEndian); var finModel = new ModelImpl(); var boneQueue = new Queue <(IBone finParentBone, JObj datBone)>(); foreach (var datRootBone in dat.RootJObjs) { boneQueue.Enqueue((finModel.Skeleton.Root, datRootBone)); } while (boneQueue.Count > 0) { var(finParentBone, datBone) = boneQueue.Dequeue(); var datBoneData = datBone.Data; var finBone = finParentBone.AddChild(datBoneData.Position.X, datBoneData.Position.Y, datBoneData.Position.Z) .SetLocalRotationRadians( datBoneData.RotationRadians.X, datBoneData.RotationRadians.Y, datBoneData.RotationRadians.Z) .SetLocalScale( datBoneData.Scale.X, datBoneData.Scale.Y, datBoneData.Scale.Z); foreach (var datChildBone in datBone.Children) { boneQueue.Enqueue((finBone, datChildBone)); } } return(finModel); }
/// <summary> /// 创建ModelElementTypeImpl实例 /// </summary> /// <param name="model">Model实例</param> /// <param name="name">ModelElement类型名称</param> /// <param name="instanceType">实例Type</param> public ModelElementTypeImpl(ModelImpl model, string name, Type instanceType) { this._model = model; this._typeName = name; this._instanceType = instanceType; }
private IModel ImportMesh(byte[] bytes) { int stride = 1; MeshNormalExportMode shadingMode = MeshNormalExportMode.Unchanged; HWBinaryResourceChunk headerChunk = GetFirstChunkOfType(HWBinaryResourceChunkType.XTD_XTDHeader); float tileScale = BinaryUtils.ReadFloatBigEndian(bytes, (int)headerChunk.Offset + 12); HWBinaryResourceChunk atlasChunk = GetFirstChunkOfType(HWBinaryResourceChunkType.XTD_AtlasChunk); int gridSize = (int)Math.Round(Math.Sqrt((atlasChunk.Size - 32) / 8)); // Subtract the min/range vector sizes, divide by position + normal size, and sqrt for grid size int positionOffset = (int)atlasChunk.Offset + 32; int normalOffset = positionOffset + gridSize * gridSize * 4; if (gridSize % stride != 0) { throw new Exception( $"Grid size {gridSize} is not evenly divisible by stride {stride} - choose a different stride value."); } // These are stored as ZYX, 4 bytes per component Vector3 PosCompMin = BinaryUtils .ReadVector3BigEndian( bytes, (int)atlasChunk.Offset) .ReverseComponents(); Vector3 PosCompRange = BinaryUtils .ReadVector3BigEndian(bytes, (int)atlasChunk.Offset + 16) .ReverseComponents(); var finModel = new ModelImpl(gridSize * gridSize); var finMesh = finModel.Skin.AddMesh(); var finVertices = new IVertex[gridSize * gridSize]; // Read vertex offsets/normals and add them to the mesh for (int x = 0; x < gridSize; x += stride) { for (int z = 0; z < gridSize; z += stride) { int index = ConvertGridPositionToIndex(new Tuple <int, int>(x, z), gridSize); int offset = index * 4; // Get offset position and normal for this vertex Vector3 position = ReadVector3Compressed(bytes, positionOffset + offset) * PosCompRange - PosCompMin; // Positions are relative to the terrain grid, so shift them by the grid position position += new Vector3(x, 0, z) * tileScale; Vector3 normal = ConvertDirectionVector( Vector3.Normalize( ReadVector3Compressed(bytes, normalOffset + offset) * 2.0f - Vector3.One)); // Simple UV based on original, non-warped terrain grid Vector3 texCoord = new Vector3(x / ((float)gridSize - 1), z / ((float)gridSize - 1), 0); var finVertex = finModel.Skin .AddVertex(position.X, position.Y, position.Z) .SetLocalNormal(normal.X, normal.Y, normal.Z) .SetUv(texCoord.X, texCoord.Y); finVertices[GetVertexIndex(x, z, gridSize)] = finVertex; } } // Generate faces based on terrain grid for (int x = 0; x < gridSize - stride; x += stride) { var triangles = new List <(IVertex, IVertex, IVertex)>(); for (int z = 0; z < gridSize - stride; z += stride) { var a = finVertices[GetVertexIndex(x, z, gridSize)]; var b = finVertices[GetVertexIndex(x + stride, z, gridSize)]; var c = finVertices[GetVertexIndex(x, z + stride, gridSize)]; var d = finVertices[GetVertexIndex(x + stride, z + stride, gridSize)]; triangles.Add((a, b, c)); triangles.Add((d, c, b)); } finMesh.AddTriangles(triangles.ToArray()); triangles.Clear(); } return(finModel); }
public unsafe IModel LoadModel(GloModelFileBundle gloModelFileBundle) { var gloFile = gloModelFileBundle.GloFile; var textureDirectories = gloModelFileBundle.TextureDirectories; var fps = 20; var glo = gloFile.Impl.ReadNew <Glo>(Endianness.LittleEndian); var textureFilesByName = new Dictionary <string, IFileHierarchyFile>(); foreach (var textureDirectory in textureDirectories) { foreach (var textureFile in textureDirectory.Files) { textureFilesByName[textureFile.Name.ToLower()] = textureFile; } } /*new MeshCsvWriter().WriteToFile( * glo, new FinFile(Path.Join(outputDirectory.FullName, "mesh.csv"))); * new FaceCsvWriter().WriteToFile( * glo, new FinFile(Path.Join(outputDirectory.FullName, "face.csv"))); * new VertexCsvWriter().WriteToFile( * glo, new FinFile(Path.Join(outputDirectory.FullName, "vertex.csv")));*/ var finModel = new ModelImpl(); var finSkin = finModel.Skin; var finRootBone = finModel.Skeleton.Root; var finTextureMap = new LazyDictionary <string, ITexture?>( textureFilename => { if (!textureFilesByName.TryGetValue(textureFilename.ToLower(), out var textureFile)) { return(null); } using var rawTextureImage = FinImage.FromFile(textureFile.Impl); var textureImageWithAlpha = GloModelLoader.AddTransparencyToGloImage_(rawTextureImage); var finTexture = finModel.MaterialManager.CreateTexture( textureImageWithAlpha); finTexture.Name = textureFilename; if (this.mirrorTextures_.Contains(textureFilename)) { finTexture.WrapModeU = WrapMode.MIRROR_REPEAT; finTexture.WrapModeV = WrapMode.MIRROR_REPEAT; } else { finTexture.WrapModeU = WrapMode.REPEAT; finTexture.WrapModeV = WrapMode.REPEAT; } return(finTexture); }); var withCullingMap = new LazyDictionary <string, IMaterial>(textureFilename => { var finTexture = finTextureMap[textureFilename]; if (finTexture == null) { return(finModel.MaterialManager.AddStandardMaterial()); } return(finModel.MaterialManager.AddTextureMaterial(finTexture)); }); var withoutCullingMap = new LazyDictionary <string, IMaterial>( textureFilename => { var finTexture = finTextureMap[textureFilename]; IMaterial finMaterial = finTexture == null ? finModel.MaterialManager .AddStandardMaterial() : finModel.MaterialManager .AddTextureMaterial( finTexture); finMaterial.CullingMode = CullingMode.SHOW_BOTH; return(finMaterial); }); var firstMeshMap = new Dictionary <string, GloMesh>(); // TODO: Consider separating these out as separate models foreach (var gloObject in glo.Objects) { var finObjectRootBone = finRootBone.AddRoot(0, 0, 0); var meshQueue = new Queue <(GloMesh, IBone)>(); foreach (var topLevelGloMesh in gloObject.Meshes) { meshQueue.Enqueue((topLevelGloMesh, finObjectRootBone)); } List <(IAnimation, int, int)> finAndGloAnimations = new(); foreach (var animSeg in gloObject.AnimSegs) { var startFrame = (int)animSeg.StartFrame; var endFrame = (int)animSeg.EndFrame; var finAnimation = finModel.AnimationManager.AddAnimation(); finAnimation.Name = animSeg.Name; finAnimation.FrameCount = (int)(animSeg.EndFrame - animSeg.StartFrame + 1); finAnimation.FrameRate = fps * animSeg.Speed; finAndGloAnimations.Add((finAnimation, startFrame, endFrame)); } while (meshQueue.Count > 0) { var(gloMesh, parentFinBone) = meshQueue.Dequeue(); var name = gloMesh.Name; GloMesh idealMesh; if (!firstMeshMap.TryGetValue(name, out idealMesh)) { firstMeshMap[name] = idealMesh = gloMesh; } var position = gloMesh.MoveKeys[0].Xyz; var rotation = gloMesh.RotateKeys[0]; var quaternion = new Quaternion(rotation.X, rotation.Y, rotation.Z, rotation.W); var xyzRadians = QuaternionUtil.ToEulerRadians(quaternion); var scale = gloMesh.ScaleKeys[0].Scale; var finBone = parentFinBone .AddChild(position.X, position.Y, position.Z) .SetLocalRotationRadians( xyzRadians.X, xyzRadians.Y, xyzRadians.Z) .SetLocalScale(scale.X, scale.Y, scale.Z); finBone.Name = name + "_bone"; var child = gloMesh.Pointers.Child; if (child != null) { meshQueue.Enqueue((child, finBone)); } var next = gloMesh.Pointers.Next; if (next != null) { meshQueue.Enqueue((next, parentFinBone)); } foreach (var(finAnimation, startFrame, endFrame) in finAndGloAnimations) { var finBoneTracks = finAnimation.AddBoneTracks(finBone); long prevTime = -1; foreach (var moveKey in gloMesh.MoveKeys) { Asserts.True(moveKey.Time > prevTime); prevTime = moveKey.Time; if (!(moveKey.Time >= startFrame && moveKey.Time <= endFrame)) { continue; } var time = (int)(moveKey.Time - startFrame); Asserts.True(time >= 0 && time < finAnimation.FrameCount); var moveValue = moveKey.Xyz; finBoneTracks.Positions.Set(time, 0, moveValue.X); finBoneTracks.Positions.Set(time, 1, moveValue.Y); finBoneTracks.Positions.Set(time, 2, moveValue.Z); } prevTime = -1; foreach (var rotateKey in gloMesh.RotateKeys) { Asserts.True(rotateKey.Time > prevTime); prevTime = rotateKey.Time; if (!(rotateKey.Time >= startFrame && rotateKey.Time <= endFrame)) { continue; } var time = (int)(rotateKey.Time - startFrame); Asserts.True(time >= 0 && time < finAnimation.FrameCount); var quaternionKey = new Quaternion(rotateKey.X, rotateKey.Y, rotateKey.Z, rotateKey.W); var xyzRadiansKey = QuaternionUtil.ToEulerRadians(quaternionKey); finBoneTracks.Rotations.Set(time, 0, xyzRadiansKey.X); finBoneTracks.Rotations.Set(time, 1, xyzRadiansKey.Y); finBoneTracks.Rotations.Set(time, 2, xyzRadiansKey.Z); } prevTime = -1; foreach (var scaleKey in gloMesh.ScaleKeys) { Asserts.True(scaleKey.Time > prevTime); prevTime = scaleKey.Time; if (!(scaleKey.Time >= startFrame && scaleKey.Time <= endFrame)) { continue; } var time = (int)(scaleKey.Time - startFrame); Asserts.True(time >= 0 && time < finAnimation.FrameCount); var scaleValue = scaleKey.Scale; finBoneTracks.Scales.Set(time, 0, scaleValue.X); finBoneTracks.Scales.Set(time, 1, scaleValue.Y); finBoneTracks.Scales.Set(time, 2, scaleValue.Z); } } // Anything with these names are debug objects and can be ignored. if (this.hiddenNames_.Contains(name)) { continue; } var finMesh = finSkin.AddMesh(); finMesh.Name = name; var gloVertices = idealMesh.Vertices; string previousTextureName = null; IMaterial?previousMaterial = null; foreach (var gloFace in idealMesh.Faces) { // TODO: What can we do if texture filename is empty? var textureFilename = gloFace.TextureFilename; var gloFaceColor = gloFace.Color; var finFaceColor = ColorImpl.FromRgbaBytes( gloFaceColor.R, gloFaceColor.G, gloFaceColor.B, gloFaceColor.A); var enableBackfaceCulling = (gloFace.Flags & 1 << 2) == 0; IMaterial?finMaterial; if (textureFilename == previousTextureName) { finMaterial = previousMaterial; } else { previousTextureName = textureFilename; finMaterial = enableBackfaceCulling ? withCullingMap[textureFilename] : withoutCullingMap[textureFilename]; previousMaterial = finMaterial; } // Face flag: // 0: potentially some kind of repeat mode?? var color = (gloFace.Flags & 1 << 6) != 0 ? ColorImpl.FromRgbaBytes(255, 0, 0, 255) : ColorImpl.FromRgbaBytes(0, 255, 0, 255); var finFaceVertices = new IVertex[3]; for (var v = 0; v < 3; ++v) { var gloVertexRef = gloFace.VertexRefs[v]; var gloVertex = gloVertices[gloVertexRef.Index]; var finVertex = finSkin .AddVertex(gloVertex.X, gloVertex.Y, gloVertex.Z) .SetUv(gloVertexRef.U, gloVertexRef.V); //.SetColor(color); finVertex.SetBoneWeights(finSkin.GetOrCreateBoneWeights( PreprojectMode.BONE, finBone)); finFaceVertices[v] = finVertex; } // TODO: Merge triangles together var finTriangles = new (IVertex, IVertex, IVertex)[1];
//JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes: //ORIGINAL LINE: @After public void tearDown() public virtual void tearDown() { ModelImpl modelImpl = (ModelImpl)modelInstance.Model; modelImpl.undeclareAlternativeNamespace(MECHANICAL_NS); }
public ModelElementTypeBuilderImpl(Type instanceType, string name, ModelImpl model) { this._instanceType = instanceType; this._model = model; _modelType = new ModelElementTypeImpl(model, name, instanceType); }
public BpmnModelInstanceImpl(ModelImpl model, ModelBuilder modelBuilder, IDomDocument document) : base(model, modelBuilder, document) { }
public IModel LoadModel(OutModelFileBundle modelFileBundle) { var outFile = modelFileBundle.OutFile; var isBw2 = modelFileBundle.GameVersion == GameVersion.BW2; Stream stream; if (isBw2) { using var gZipStream = new GZipStream(outFile.Impl.OpenRead(), CompressionMode.Decompress); stream = new MemoryStream(); gZipStream.CopyTo(stream); stream.Position = 0; } else { stream = outFile.Impl.OpenRead(); } using var er = new EndianBinaryReader(stream, Endianness.LittleEndian); var bwHeightmap = isBw2 ? (IBwHeightmap)er.ReadNew <Bw2Heightmap>() : er.ReadNew <Bw1Heightmap>(); var finModel = new ModelImpl(); var finSkin = finModel.Skin; var finMesh = finSkin.AddMesh(); var triangles = new List <(IVertex, IVertex, IVertex)>(); var chunks = bwHeightmap.Chunks; var heightmapWidth = 64 * 4 * 4; var heightmapHeight = 64 * 4 * 4; var chunkFinVertices = new Grid <IVertex?>(heightmapWidth, heightmapHeight); var heights = new Grid <ushort>(heightmapWidth, heightmapHeight); for (var chunkY = 0; chunkY < chunks.Height; ++chunkY) { for (var chunkX = 0; chunkX < chunks.Width; ++chunkX) { var tiles = chunks[chunkX, chunkY]?.Tiles; if (tiles == null) { continue; } for (var tileY = 0; tileY < tiles.Height; ++tileY) { for (var tileX = 0; tileX < tiles.Width; ++tileX) { var points = tiles[tileX, tileY].Points; for (var pointY = 0; pointY < points.Height; ++pointY) { for (var pointX = 0; pointX < points.Width; ++pointX) { var point = points[pointX, pointY]; var heightmapX = 16 * chunkX + 4 * tileX + pointX; var heightmapY = 16 * chunkY + 4 * tileY + pointY; var finVertex = finSkin.AddVertex(point.X, point.Height, point.Y) .SetUv(1f * heightmapX / heightmapWidth, 1f * heightmapY / heightmapHeight); chunkFinVertices[heightmapX, heightmapY] = finVertex; heights[heightmapX, heightmapY] = point.Height; } } } } } } var image = new I8Image(heightmapWidth, heightmapHeight); image.Mutate((_, setHandler) => { for (var vY = 0; vY < heightmapHeight; ++vY) { for (var vX = 0; vX < heightmapWidth; ++vX) { setHandler(vX, vY, (byte)(heights[vX, vY] / 24)); } } }); var heightmapTexture = finModel.MaterialManager.CreateTexture(image); var finMaterial = finModel.MaterialManager.AddTextureMaterial(heightmapTexture); for (var vY = 0; vY < heightmapHeight - 1; ++vY) { for (var vX = 0; vX < heightmapWidth - 1; ++vX) { var a = chunkFinVertices[vX, vY]; var b = chunkFinVertices[vX + 1, vY]; var c = chunkFinVertices[vX, vY + 1]; var d = chunkFinVertices[vX + 1, vY + 1]; if (a != null && b != null && c != null && d != null) { triangles.Add((a, b, c)); triangles.Add((d, c, b)); } } } finMesh.AddTriangles(triangles.ToArray()).SetMaterial(finMaterial); return(finModel); }
// TODO: Split these out into separate classes // TODO: Reading from the file here is gross public IModel LoadModel(CmbModelFileBundle modelFileBundle) { var cmbFile = modelFileBundle.CmbFile; var csabFiles = modelFileBundle.CsabFiles; var ctxbFiles = modelFileBundle.CtxbFiles; var shpaFiles = modelFileBundle.ShpaFiles; var fps = 30; using var r = new EndianBinaryReader(cmbFile.Impl.OpenRead(), Endianness.LittleEndian); var cmb = new Cmb(r); r.Position = 0; var filesAndCsabs = csabFiles?.Select(csabFile => { var csab = csabFile.Impl.ReadNew <Csab>(Endianness.LittleEndian); return(csabFile, csab); }) .ToList() ?? new List <(IFileHierarchyFile shpaFile, Csab csab)>(); var filesAndCtxbs = ctxbFiles?.Select(ctxbFile => { var ctxb = ctxbFile.Impl.ReadNew <Ctxb>(Endianness.LittleEndian); return(ctxbFile, ctxb); }) .ToList() ?? new List <(IFileHierarchyFile shpaFile, Ctxb ctxb)>(); var filesAndShpas = shpaFiles?.Select(shpaFile => { var shpa = shpaFile.Impl.ReadNew <Shpa>(Endianness.LittleEndian); return(shpaFile, shpa); }) .ToList() ?? new List <(IFileHierarchyFile shpaFile, Shpa shpa)>(); var finModel = new ModelImpl(); var finSkin = finModel.Skin; // Adds bones var finBones = new IBone[cmb.skl.bones.Length]; var boneQueue = new Queue <(Bone, IBone?)>(); boneQueue.Enqueue((cmb.skl.bones[0], null)); while (boneQueue.Count > 0) { var(cmbBone, finBoneParent) = boneQueue.Dequeue(); var translation = cmbBone.translation; var radians = cmbBone.rotation; var scale = cmbBone.scale; var finBone = (finBoneParent ?? finModel.Skeleton.Root) .AddChild(translation.X, translation.Y, translation.Z) .SetLocalRotationRadians(radians.X, radians.Y, radians.Z) .SetLocalScale(scale.X, scale.Y, scale.Z); finBones[cmbBone.id] = finBone; foreach (var child in cmbBone.children) { boneQueue.Enqueue((child, finBone)); } } // Adds animations foreach (var(csabFile, csab) in filesAndCsabs) { var finAnimation = finModel.AnimationManager.AddAnimation(); finAnimation.Name = csabFile.NameWithoutExtension; finAnimation.FrameCount = (int)csab.Duration; finAnimation.FrameRate = fps; foreach (var(boneIndex, anod) in csab.BoneIndexToAnimationNode) { var boneTracks = finAnimation.AddBoneTracks(finBones[boneIndex]); // TODO: Add support for in/out tangents foreach (var translationX in anod.TranslationX.Keyframes) { boneTracks.Positions.Set((int)translationX.Time, 0, translationX.Value, translationX.IncomingTangent, translationX.OutgoingTangent); } foreach (var translationY in anod.TranslationY.Keyframes) { boneTracks.Positions.Set((int)translationY.Time, 1, translationY.Value, translationY.IncomingTangent, translationY.OutgoingTangent); } foreach (var translationZ in anod.TranslationZ.Keyframes) { boneTracks.Positions.Set((int)translationZ.Time, 2, translationZ.Value, translationZ.IncomingTangent, translationZ.OutgoingTangent); } foreach (var scaleX in anod.ScaleX.Keyframes) { boneTracks.Scales.Set((int)scaleX.Time, 0, scaleX.Value, scaleX.IncomingTangent, scaleX.OutgoingTangent); } foreach (var scaleY in anod.ScaleY.Keyframes) { boneTracks.Scales.Set((int)scaleY.Time, 1, scaleY.Value, scaleY.IncomingTangent, scaleY.OutgoingTangent); } foreach (var scaleZ in anod.ScaleZ.Keyframes) { boneTracks.Scales.Set((int)scaleZ.Time, 2, scaleZ.Value, scaleZ.IncomingTangent, scaleZ.OutgoingTangent); } foreach (var rotationX in anod.RotationX.Keyframes) { boneTracks.Rotations.Set((int)rotationX.Time, 0, rotationX.Value, rotationX.IncomingTangent, rotationX.OutgoingTangent); } foreach (var rotationY in anod.RotationY.Keyframes) { boneTracks.Rotations.Set((int)rotationY.Time, 1, rotationY.Value, rotationY.IncomingTangent, rotationY.OutgoingTangent); } foreach (var rotationZ in anod.RotationZ.Keyframes) { boneTracks.Rotations.Set((int)rotationZ.Time, 2, rotationZ.Value, rotationZ.IncomingTangent, rotationZ.OutgoingTangent); } } } // TODO: Move these reads into the model reading logic var ctrTexture = new CtrTexture(); var textureImages = cmb.tex.textures.Select(cmbTexture => { var position = cmb.startOffset + cmb.header.textureDataOffset + cmbTexture.dataOffset; IImage image; if (position != 0) { r.Position = position; var data = r.ReadBytes((int)cmbTexture.dataLength); image = ctrTexture.DecodeImage(data, cmbTexture); } else { var ctxb = filesAndCtxbs .Select( fileAndCtxb => fileAndCtxb.Item2) .Single(ctxb => ctxb.Chunk.Entry.name == cmbTexture.name); image = ctrTexture.DecodeImage(ctxb.Data, cmbTexture); } return(image); }) .ToArray(); // Creates meshes & textures // TODO: Emulate fixed-function materials var finMaterials = new List <IMaterial>(); for (var i = 0; i < cmb.mat.materials.Length; ++i) { var cmbMaterial = cmb.mat.materials[i]; // Get associated texture var texMapper = cmbMaterial.texMappers[0]; var textureId = texMapper.textureId; ITexture?finTexture = null; if (textureId != -1) { var cmbTexture = cmb.tex.textures[textureId]; var textureImage = textureImages[textureId]; finTexture = finModel.MaterialManager.CreateTexture(textureImage); finTexture.Name = cmbTexture.name; finTexture.WrapModeU = this.CmbToFinWrapMode(texMapper.wrapS); finTexture.WrapModeV = this.CmbToFinWrapMode(texMapper.wrapT); } // Create material IMaterial finMaterial = finTexture != null ? finModel.MaterialManager .AddTextureMaterial( finTexture) : finModel.MaterialManager .AddLayerMaterial(); finMaterial.Name = $"material{i}"; finMaterial.CullingMode = cmbMaterial.faceCulling switch { CullMode.FrontAndBack => CullingMode.SHOW_BOTH, CullMode.Front => CullingMode.SHOW_FRONT_ONLY, CullMode.BackFace => CullingMode.SHOW_BACK_ONLY, CullMode.Never => CullingMode.SHOW_NEITHER, _ => throw new NotImplementedException(), }; finMaterials.Add(finMaterial); } /*{ * var nameToTextures = new Dictionary<string, ITexture>(); * foreach (var finMaterial in finMaterials) { * foreach (var finTexture in finMaterial.Textures) { * nameToTextures[finTexture.Name] = finTexture; * } * } * * foreach (var (_, finTexture) in nameToTextures) { * finTexture.ImageData.Save( * Path.Join(outputDirectory.FullName, finTexture.Name + ".png")); * } * }*/ var verticesByIndex = new ListDictionary <int, IVertex>(); // Adds meshes foreach (var cmbMesh in cmb.sklm.meshes.meshes) { var shape = cmb.sklm.shapes.shapes[cmbMesh.shapeIndex]; uint vertexCount = 0; var meshIndices = new List <uint>(); foreach (var pset in shape.primitiveSets) { foreach (var index in pset.primitive.indices) { meshIndices.Add(index); vertexCount = Math.Max(vertexCount, index); } } ++vertexCount; var preproject = new bool?[vertexCount]; var skinningModes = new SkinningMode?[vertexCount]; foreach (var pset in shape.primitiveSets) { foreach (var index in pset.primitive.indices) { skinningModes[index] = pset.skinningMode; preproject[index] = pset.skinningMode != SkinningMode.Smooth; } } // Gets flags var inc = 1; var hasNrm = BitLogic.GetFlag(shape.vertFlags, inc++); if (cmb.header.version > CmbVersion.OCARINA_OF_TIME_3D) { // Skip "HasTangents" for now inc++; } var hasClr = BitLogic.GetFlag(shape.vertFlags, inc++); var hasUv0 = BitLogic.GetFlag(shape.vertFlags, inc++); var hasUv1 = BitLogic.GetFlag(shape.vertFlags, inc++); var hasUv2 = BitLogic.GetFlag(shape.vertFlags, inc++); var hasBi = BitLogic.GetFlag(shape.vertFlags, inc++); var hasBw = BitLogic.GetFlag(shape.vertFlags, inc++); // Gets bone indices var boneCount = shape.boneDimensions; var bIndices = new short[vertexCount * boneCount]; foreach (var pset in shape.primitiveSets) { foreach (var i in pset.primitive.indices) { if (hasBi && pset.skinningMode != SkinningMode.Single) { r.Position = cmb.startOffset + cmb.header.vatrOffset + cmb.vatr.bIndices.StartOffset + shape.bIndices.Start + i * DataTypeUtil.GetSize(shape.bIndices.DataType) * shape.boneDimensions; for (var bi = 0; bi < shape.boneDimensions; ++bi) { var boneTableIndex = shape.bIndices.Scale * DataTypeUtil.Read( r, shape.bIndices.DataType); bIndices[i * boneCount + bi] = pset.boneTable[(int)boneTableIndex]; } } else { bIndices[i] = shape.primitiveSets[0].boneTable[0]; } } } var finMesh = finSkin.AddMesh(); // TODO: Encapsulate these reads somewhere else // Get vertices var finVertices = new IVertex[vertexCount]; for (var i = 0; i < vertexCount; ++i) { // Position r.Position = cmb.startOffset + cmb.header.vatrOffset + cmb.vatr.position.StartOffset + shape.position.Start + 3 * DataTypeUtil.GetSize(shape.position.DataType) * i; var positionValues = DataTypeUtil.Read(r, 3, shape.position.DataType) .Select(value => value * shape.position.Scale) .ToArray(); var finVertex = finSkin.AddVertex(positionValues[0], positionValues[1], positionValues[2]); finVertices[i] = finVertex; var index = (ushort)(shape.position.Start / 3 + i); verticesByIndex.Add(index, finVertex); if (hasNrm) { r.Position = cmb.startOffset + cmb.header.vatrOffset + cmb.vatr.normal.StartOffset + shape.normal.Start + 3 * DataTypeUtil.GetSize(shape.normal.DataType) * i; var normalValues = DataTypeUtil.Read(r, 3, shape.normal.DataType) .Select(value => value * shape.normal.Scale) .ToArray(); finVertex.SetLocalNormal(normalValues[0], normalValues[1], normalValues[2]); } if (hasClr) { r.Position = cmb.startOffset + cmb.header.vatrOffset + cmb.vatr.color.StartOffset + shape.color.Start + 4 * DataTypeUtil.GetSize(shape.color.DataType) * i; var colorValues = DataTypeUtil.Read(r, 4, shape.color.DataType) .Select(value => value * shape.color.Scale) .ToArray(); finVertex.SetColorBytes((byte)(colorValues[0] * 255), (byte)(colorValues[1] * 255), (byte)(colorValues[2] * 255), (byte)(colorValues[3] * 255)); } if (hasUv0) { r.Position = cmb.startOffset + cmb.header.vatrOffset + cmb.vatr.uv0.StartOffset + shape.uv0.Start + 2 * DataTypeUtil.GetSize(shape.uv0.DataType) * i; var uv0Values = DataTypeUtil.Read(r, 2, shape.uv0.DataType) .Select(value => value * shape.uv0.Scale) .ToArray(); finVertex.SetUv(0, uv0Values[0], 1 - uv0Values[1]); } if (hasUv1) { r.Position = cmb.startOffset + cmb.header.vatrOffset + cmb.vatr.uv1.StartOffset + shape.uv1.Start + 2 * DataTypeUtil.GetSize(shape.uv1.DataType) * i; var uv1Values = DataTypeUtil.Read(r, 2, shape.uv1.DataType) .Select(value => value * shape.uv1.Scale) .ToArray(); finVertex.SetUv(1, uv1Values[0], 1 - uv1Values[1]); } if (hasUv2) { r.Position = cmb.startOffset + cmb.header.vatrOffset + cmb.vatr.uv2.StartOffset + shape.uv2.Start + 2 * DataTypeUtil.GetSize(shape.uv2.DataType) * i; var uv2Values = DataTypeUtil.Read(r, 2, shape.uv2.DataType) .Select(value => value * shape.uv2.Scale) .ToArray(); finVertex.SetUv(2, uv2Values[0], 1 - uv2Values[1]); } var preprojectMode = preproject[i].Value ? PreprojectMode.BONE : PreprojectMode.NONE; if (hasBw) { r.Position = cmb.startOffset + cmb.header.vatrOffset + cmb.vatr.bWeights.StartOffset + shape.bWeights.Start + i * DataTypeUtil.GetSize(shape.bWeights.DataType) * boneCount; var totalWeight = 0f; var boneWeights = new List <BoneWeight>(); for (var j = 0; j < boneCount; ++j) { // TODO: Looks like this is rounded to the nearest 2 in the original?? var weight = DataTypeUtil.Read(r, shape.bWeights.DataType) * shape.bWeights.Scale; totalWeight += weight; if (weight > 0) { var bone = finBones[bIndices[i * boneCount + j]]; var boneWeight = new BoneWeight(bone, null, weight); boneWeights.Add(boneWeight); } } Asserts.True(boneWeights.Count > 0); Asserts.True(Math.Abs(1 - totalWeight) < .0001); finVertex.SetBoneWeights( finSkin.GetOrCreateBoneWeights(preprojectMode, boneWeights.ToArray())); } else { var boneIndex = bIndices[i]; finVertex.SetBoneWeights( finSkin.GetOrCreateBoneWeights(preprojectMode, finBones[boneIndex])); } finVertex.SetColor(ColorImpl.FromSystemColor(Color.White)); } // Adds faces. Thankfully, it's all just triangles! var triangleVertices = meshIndices .Select(meshIndex => finVertices[meshIndex]) .ToArray(); finMesh.AddTriangles(triangleVertices) .SetMaterial(finMaterials[cmbMesh.materialIndex]) .SetVertexOrder(VertexOrder.NORMAL); } // Adds morph targets foreach (var(shpaFile, shpa) in filesAndShpas) { var shpaIndexToPosi = shpa?.Posi.Values.Select((posi, i) => (shpa.Idxs.Indices[i], posi)) .ToDictionary(indexAndPosi => indexAndPosi.Item1, indexAndPosi => indexAndPosi.posi); var morphTarget = finModel.AnimationManager.AddMorphTarget(); morphTarget.Name = shpaFile.NameWithoutExtension; foreach (var(index, position) in shpaIndexToPosi) { if (!verticesByIndex.TryGetList(index, out var finVertices)) { continue; } foreach (var finVertex in finVertices) { morphTarget.MoveTo(finVertex, position); } } } return(finModel); }
public IModel LoadModel(ModlModelFileBundle modelFileBundle) { var flipSign = ModlFlags.FLIP_HORIZONTALLY ? -1 : 1; var modlFile = modelFileBundle.ModlFile; using var er = new EndianBinaryReader(modlFile.Impl.OpenRead(), Endianness.LittleEndian); var bwModel = modelFileBundle.ModlType switch { ModlType.BW1 => (IModl)er.ReadNew <Bw1Modl>(), ModlType.BW2 => er.ReadNew <Bw2Modl>(), }; var model = new ModelImpl(); var finMesh = model.Skin.AddMesh(); var finBones = new IBone[bwModel.Nodes.Count]; var finBonesByModlNode = new Dictionary <IBwNode, IBone>(); var finBonesByIdentifier = new Dictionary <string, IBone>(); { var nodeQueue = new FinTuple2Queue <IBone, ushort>((model.Skeleton.Root, 0)); while (nodeQueue.TryDequeue(out var parentFinBone, out var modlNodeId)) { var modlNode = bwModel.Nodes[modlNodeId]; var transform = modlNode.Transform; var bonePosition = transform.Position; var modlRotation = transform.Rotation; var rotation = new Quaternion( flipSign * modlRotation.X, modlRotation.Y, modlRotation.Z, flipSign * modlRotation.W); var eulerRadians = QuaternionUtil.ToEulerRadians(rotation); var finBone = parentFinBone .AddChild(flipSign * bonePosition.X, bonePosition.Y, bonePosition.Z) .SetLocalRotationRadians( eulerRadians.X, eulerRadians.Y, eulerRadians.Z); var identifier = modlNode.GetIdentifier(); finBone.Name = identifier; finBones[modlNodeId] = finBone; finBonesByModlNode[modlNode] = finBone; finBonesByIdentifier[identifier] = finBone; if (bwModel.CnctParentToChildren.TryGetList( modlNodeId, out var modlChildIds)) { nodeQueue.Enqueue( modlChildIds !.Select(modlChildId => (finBone, modlChildId))); } } foreach (var animFile in modelFileBundle.AnimFiles ?? Array.Empty <IFileHierarchyFile>()) { var anim = modelFileBundle.ModlType switch { ModlType.BW1 => (IAnim)animFile.Impl.ReadNew <Bw1Anim>( Endianness.BigEndian), ModlType.BW2 => animFile.Impl.ReadNew <Bw2Anim>( Endianness.BigEndian) }; var maxFrameCount = -1; foreach (var animBone in anim.AnimBones) { maxFrameCount = (int)Math.Max(maxFrameCount, Math.Max( animBone .PositionKeyframeCount, animBone .RotationKeyframeCount)); } var finAnimation = model.AnimationManager.AddAnimation(); finAnimation.Name = animFile.NameWithoutExtension; finAnimation.FrameRate = 30; finAnimation.FrameCount = maxFrameCount; for (var b = 0; b < anim.AnimBones.Count; ++b) { var animBone = anim.AnimBones[b]; var animBoneFrames = anim.AnimBoneFrames[b]; var animNodeIdentifier = animBone.GetIdentifier(); if (!finBonesByIdentifier.TryGetValue( animNodeIdentifier, out var finBone)) { // TODO: Gross hack for the vet models, what's the real fix??? if (animNodeIdentifier == Bw1Node.GetIdentifier(33)) { finBone = finBonesByIdentifier[Bw1Node.GetIdentifier(34)]; } else if (finBonesByIdentifier.TryGetValue( animNodeIdentifier + 'X', out var xBone)) { finBone = xBone; } else if (finBonesByIdentifier.TryGetValue( "BONE_" + animNodeIdentifier, out var prefixBone)) { finBone = prefixBone; } else if (animNodeIdentifier == "WF_GRUNT_BACKPAC") { // TODO: Is this right????? finBone = finBonesByIdentifier["BONE_BCK_MISC"]; } else { ; } } var finBoneTracks = finAnimation.AddBoneTracks(finBone !); var fbtPositions = finBoneTracks.Positions; for (var f = 0; f < animBone.PositionKeyframeCount; ++f) { var(fPX, fPY, fPZ) = animBoneFrames.PositionFrames[f]; fbtPositions.Set(f, 0, flipSign * fPX); fbtPositions.Set(f, 1, fPY); fbtPositions.Set(f, 2, fPZ); } var fbtRotations = finBoneTracks.Rotations; for (var f = 0; f < animBone.RotationKeyframeCount; ++f) { var(fRX, fRY, fRZ, frW) = animBoneFrames.RotationFrames[f]; var animationQuaternion = new Quaternion(flipSign * fRX, fRY, fRZ, flipSign * frW); var eulerRadians = QuaternionUtil.ToEulerRadians(animationQuaternion); fbtRotations.Set(f, 0, eulerRadians.X); fbtRotations.Set(f, 1, eulerRadians.Y); fbtRotations.Set(f, 2, eulerRadians.Z); } } } var textureDictionary = new LazyDictionary <string, ITexture>( textureName => { var textureFile = modlFile.Parent.Files.Single( file => file.Name.ToLower() == $"{textureName}.png"); var image = FinImage.FromFile(textureFile.Impl); var finTexture = model.MaterialManager.CreateTexture(image); finTexture.Name = textureName; // TODO: Need to handle wrapping finTexture.WrapModeU = WrapMode.REPEAT; finTexture.WrapModeV = WrapMode.REPEAT; return(finTexture); }); foreach (var modlNode in bwModel.Nodes) { var finMaterials = modlNode.Materials.Select(modlMaterial => { var textureName = modlMaterial.Texture1.ToLower(); if (textureName == "") { return(null); } var finTexture = textureDictionary[textureName]; var finMaterial = model.MaterialManager .AddTextureMaterial(finTexture); return(finMaterial); }) .ToArray(); foreach (var modlMesh in modlNode.Meshes) { var finMaterial = finMaterials[modlMesh.MaterialIndex]; foreach (var triangleStrip in modlMesh.TriangleStrips) { var vertices = new IVertex[triangleStrip.VertexAttributeIndicesList.Count]; for (var i = 0; i < vertices.Length; i++) { var vertexAttributeIndices = triangleStrip.VertexAttributeIndicesList[i]; var position = modlNode.Positions[vertexAttributeIndices.PositionIndex]; var vertex = vertices[i] = model.Skin.AddVertex( flipSign * position.X * modlNode.Scale, position.Y * modlNode.Scale, position.Z * modlNode.Scale); if (vertexAttributeIndices.NormalIndex != null) { var normal = modlNode.Normals[ vertexAttributeIndices.NormalIndex.Value]; vertex.SetLocalNormal(flipSign * normal.X, normal.Y, normal.Z); } if (vertexAttributeIndices.NodeIndex != null) { var finBone = finBones[vertexAttributeIndices.NodeIndex.Value]; vertex.SetBoneWeights( model.Skin .GetOrCreateBoneWeights( PreprojectMode.NONE, new BoneWeight(finBone, null, 1))); } else { var finBone = finBonesByModlNode[modlNode]; vertex.SetBoneWeights( model.Skin.GetOrCreateBoneWeights( PreprojectMode.BONE, finBone)); } var texCoordIndex0 = vertexAttributeIndices.TexCoordIndices[0]; var texCoordIndex1 = vertexAttributeIndices.TexCoordIndices[1]; if (texCoordIndex1 != null) { int texCoordIndex; if (texCoordIndex0 != null) { texCoordIndex = (texCoordIndex0.Value << 8) | texCoordIndex1.Value; } else { texCoordIndex = texCoordIndex1.Value; } var uv = modlNode.UvMaps[0][texCoordIndex]; vertex.SetUv(uv.U, uv.V); } } var triangleStripPrimitive = finMesh.AddTriangleStrip(vertices); if (finMaterial != null) { triangleStripPrimitive.SetMaterial(finMaterial); } } } } } return(model); } }
//TODO Attribute Id,Name等解析 public DmnModelInstanceImpl(ModelImpl model, ModelBuilder modelBuilder, IDomDocument document) : base(model, modelBuilder, document) { //var test = Definitions; }