private void LoadTagDataFromFile(EndianBinaryReader reader, int tagCount, bool dumpTextures, bool dumpShaders) { for (int i = 0; i < tagCount; i++) { long tagStart = reader.BaseStream.Position; string tagName = reader.ReadString(4); int tagSize = reader.ReadInt32(); switch (tagName) { // INFO - Vertex Count, Scene Hierarchy case "INF1": INF1Tag = new INF1(); INF1Tag.LoadINF1FromStream(reader, tagStart); break; // VERTEX - Stores vertex arrays for pos/normal/color0/tex0 etc. // Contains VertexAttributes which describe how the data is stored/laid out. case "VTX1": VTX1Tag = new VTX1(); VTX1Tag.LoadVTX1FromStream(reader, tagStart, tagSize); break; // ENVELOPES - Defines vertex weights for skinning case "EVP1": EVP1Tag = new EVP1(); EVP1Tag.LoadEVP1FromStream(reader, tagStart); break; // DRAW (Skeletal Animation Data) - Stores which matrices (?) are weighted, and which are used directly case "DRW1": DRW1Tag = new DRW1(); DRW1Tag.LoadDRW1FromStream(reader, tagStart); break; // JOINTS - Stores the skeletal joints (position, rotation, scale, etc...) case "JNT1": JNT1Tag = new JNT1(); JNT1Tag.LoadJNT1FromStream(reader, tagStart); JNT1Tag.CalculateParentJointsForSkeleton(INF1Tag.HierarchyRoot); break; // SHAPE - Face/Triangle information for model. case "SHP1": SHP1Tag = new SHP1(); SHP1Tag.ReadSHP1FromStream(reader, tagStart); CalculateModelBounds(); break; // MATERIAL - Stores materials (which describes how textures, etc. are drawn) case "MAT3": MAT3Tag = new MAT3(); MAT3Tag.LoadMAT3FromStream(reader, tagStart); break; // TEXTURES - Stores binary texture images. case "TEX1": TEX1Tag = new TEX1(); TEX1Tag.LoadTEX1FromStream(reader, tagStart, dumpTextures); break; // MODEL - Seems to be bypass commands for Materials and invokes GX registers directly. case "MDL3": break; } // Skip the stream reader to the start of the next tag since it gets moved around during loading. reader.BaseStream.Position = tagStart + tagSize; } INF1Tag.LinkData(MAT3Tag, SHP1Tag); Tick(float.Epsilon); if (EVP1Tag.InverseBindPose.Count <= 0) { EVP1Tag.GenerateInverseBindMatrices(JNT1Tag); } DRW1Tag.UpdateMatrices(JNT1Tag.BindJoints, EVP1Tag); SHP1Tag.LinkData(VTX1Tag, DRW1Tag, EVP1Tag); SHP1Tag.UploadShapesToGPU(); }
private void BindMaterialByIndex(ushort index, bool bDepthOnlyPrePass) { MAT3 mat3 = MAT3Tag; TEX1 tex1 = TEX1Tag; if (m_currentExternalMaterial != null) { if (m_currentExternalMaterial.MaterialsCount != 0) { mat3 = m_currentExternalMaterial.MAT3; } if (m_currentExternalMaterial.TexturesCount != 0) { tex1 = m_currentExternalMaterial.TEX1; } } // While the game collapses duplicate materials via the material index remap table, // the actual original names are preserved with their original indexes through the // string table. string materialName = mat3.MaterialNameTable[index]; Material material = mat3.MaterialList[index]; material.Bind(); m_currentBoundMat = material; Shader shader = material.Shader; //GL.UniformMatrix4(shader.UniformModelMtx, false, ref m_modelMatrix); //GL.UniformMatrix4(shader.UniformViewMtx, false, ref m_viewMatrix); //GL.UniformMatrix4(shader.UniformProjMtx, false, ref m_projMatrix); for (int i = 0; i < 8; i++) { int idx = material.TextureIndices[i]; if (idx < 0) { continue; } //int glTextureIndex = GL.GetUniformLocation(shader.Program, string.Format("Texture[{0}]", i)); Texture tex = tex1.Textures[mat3.TextureRemapTable[idx]]; // Before we bind the texture, we need to check if this particular texture has been overriden. // This allows textures to be replaced on a per-name basis with another file. Used in cases of // broken/incorrect texture included by default in models, ie: The Wind Waker toon textures. if (m_textureOverrides.ContainsKey(tex.Name)) { tex = m_textureOverrides[tex.Name]; } GL.Uniform1(shader.UniformTextureSamplers[i], i); // Everything dies without this, don't forget this bit. tex.Bind(i); } /*if (shader.UniformTexMtx >= 0) * { * for (int i = 0; i < material.TexMatrixIndexes.Length; i++) * { * Matrix4 matrix = material.TexMatrixIndexes[i].TexMtx; * string matrixString = string.Format("TexMtx[{0}]", i); * if(material.TexMatrixIndexes[i].MatrixUniformLocationForGPU < 0) * { * // If the shader's broke this will try to re-assign every frame, but should vastly improve it most use cases. * material.TexMatrixIndexes[i].MatrixUniformLocationForGPU = GL.GetUniformLocation(shader.Program, matrixString); * } * * GL.UniformMatrix4(material.TexMatrixIndexes[i].MatrixUniformLocationForGPU, true, ref matrix); * } * } * * if(shader.UniformPostTexMtx >= 0) * { * for(int i = 0; i < material.PostTexMatrixIndexes.Length; i++) * { * Matrix4 matrix = material.PostTexMatrixIndexes[i].TexMtx; * * string matrixString = string.Format("PostMtx[{0}]", i); * * if (material.PostTexMatrixIndexes[i].MatrixUniformLocationForGPU == 0) * { * material.PostTexMatrixIndexes[i].MatrixUniformLocationForGPU = GL.GetUniformLocation(shader.Program, matrixString); * } * * GL.UniformMatrix4(material.PostTexMatrixIndexes[i].MatrixUniformLocationForGPU, true, ref matrix); * } * }*/ var color0Amb = material.AmbientColors[0]; var color0Mat = material.MaterialColors[0]; var color1Amb = material.AmbientColors[1]; var color1Mat = material.MaterialColors[1]; //if (shader.UniformColor0Amb >= 0) GL.Uniform4(shader.UniformColor0Amb, color0Amb.R, color0Amb.G, color0Amb.B, color0Amb.A); //if (shader.UniformColor0Mat >= 0) GL.Uniform4(shader.UniformColor0Mat, color0Mat.R, color0Mat.G, color0Mat.B, color0Mat.A); //if (shader.UniformColor1Amb >= 0) GL.Uniform4(shader.UniformColor1Amb, color1Amb.R, color1Amb.G, color1Amb.B, color1Amb.A); //if (shader.UniformColor1Mat >= 0) GL.Uniform4(shader.UniformColor1Mat, color1Mat.R, color1Mat.G, color1Mat.B, color1Mat.A); // Set the OpenGL State GXToOpenGL.SetBlendState(material.BlendMode); GXToOpenGL.SetCullState(material.CullMode); GXToOpenGL.SetDepthState(material.ZMode, bDepthOnlyPrePass); GXToOpenGL.SetDitherEnabled(material.Dither); // Check to see if we've overridden the material's ability to write to the color channel. This is used // to add support for bmd/bdl models who have this setting changed through game-code since the bmd/bdl // format does not appear to otherwise specify. if (m_colorWriteOverrides.ContainsKey(materialName)) { bool enabled = m_colorWriteOverrides[materialName]; GL.ColorMask(enabled, enabled, enabled, true); } else if (bDepthOnlyPrePass) { GL.ColorMask(false, false, false, false); } else { GL.ColorMask(true, true, true, true); } //if (WInput.GetKey(System.Windows.Input.Key.U)) // GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line); //else // GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill); // Update the data in the PS Block PSBlock psData = new PSBlock(); m_tevColorOverrides.SetPSBlockForMaterial(material, ref psData); UpdateTextureDimensionsForPSBlock(ref psData, material); UpdateFogForPSBlock(ref psData, material); // Upload the PS Block to the GPU GL.BindBufferBase(BufferRangeTarget.UniformBuffer, (int)ShaderUniformBlockIds.PixelShaderBlock, shader.PSBlockUBO); GL.BufferData <PSBlock>(BufferTarget.UniformBuffer, (IntPtr)(Marshal.SizeOf(psData)), ref psData, BufferUsageHint.DynamicDraw); }
private void LoadTagDataFromFile(EndianBinaryReader reader, int tagCount, bool dumpTextures, bool dumpShaders) { for (int i = 0; i < tagCount; i++) { long tagStart = reader.BaseStream.Position; string tagName = reader.ReadString(4); int tagSize = reader.ReadInt32(); switch (tagName) { // INFO - Vertex Count, Scene Hierarchy case "INF1": INF1Tag = new INF1(); INF1Tag.LoadINF1FromStream(reader, tagStart); break; // VERTEX - Stores vertex arrays for pos/normal/color0/tex0 etc. // Contains VertexAttributes which describe how the data is stored/laid out. case "VTX1": VTX1Tag = new VTX1(); VTX1Tag.LoadVTX1FromStream(reader, tagStart, tagSize); break; // ENVELOPES - Defines vertex weights for skinning case "EVP1": EVP1Tag = new EVP1(); EVP1Tag.LoadEVP1FromStream(reader, tagStart); break; // DRAW (Skeletal Animation Data) - Stores which matrices (?) are weighted, and which are used directly case "DRW1": DRW1Tag = new DRW1(); DRW1Tag.LoadDRW1FromStream(reader, tagStart); break; // JOINTS - Stores the skeletal joints (position, rotation, scale, etc...) case "JNT1": JNT1Tag = new JNT1(); JNT1Tag.LoadJNT1FromStream(reader, tagStart); JNT1Tag.CalculateParentJointsForSkeleton(INF1Tag.HierarchyRoot); break; // SHAPE - Face/Triangle information for model. case "SHP1": SHP1Tag = new SHP1(); SHP1Tag.ReadSHP1FromStream(reader, tagStart, VTX1Tag.VertexData); break; // MATERIAL - Stores materials (which describes how textures, etc. are drawn) case "MAT3": MAT3Tag = new MAT3(); MAT3Tag.LoadMAT3FromStream(reader, tagStart); break; // TEXTURES - Stores binary texture images. case "TEX1": TEX1Tag = new TEX1(); TEX1Tag.LoadTEX1FromStream(reader, tagStart, dumpTextures); break; // MODEL - Seems to be bypass commands for Materials and invokes GX registers directly. case "MDL3": break; } // Skip the stream reader to the start of the next tag since it gets moved around during loading. reader.BaseStream.Position = tagStart + tagSize; } // To generate shaders we need to know which vertex attributes need to be enabled for the shader. However, // the shader has no knowledge in our book as to what attributes are enabled. Theoretically we could enable // them on the fly as something requested it, but that'd involve more code that I don't want to do right now. // To resolve, we iterate once through the hierarchy to see which mesh is called after a material and bind the // vertex descriptions. Material dummyMat = null; AssignVertexAttributesToMaterialsRecursive(INF1Tag.HierarchyRoot, ref dummyMat, MAT3Tag); // Now that the vertex attributes are assigned to the materials, generate a shader from the data. GenerateShadersForMaterials(MAT3Tag, dumpShaders); // Iterate through the shapes and calculate a bounding box which encompasses all of them. Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue); foreach (var shape in SHP1Tag.Shapes) { Vector3 sMin = shape.BoundingBox.Min; Vector3 sMax = shape.BoundingBox.Max; if (sMin.X < min.X) { min.X = sMin.X; } if (sMax.X > max.X) { max.X = sMax.X; } if (sMin.Y < min.Y) { min.Y = sMin.Y; } if (sMax.Y > max.Y) { max.Y = sMax.Y; } if (sMin.Z < min.Z) { min.Z = sMin.Z; } if (sMax.Z > max.Z) { max.Z = sMax.Z; } } BoundingBox = new FAABox(min, max); BoundingSphere = new FSphere(BoundingBox.Center, BoundingBox.Max.Length); }