private static Attach FromNoWeight(Mesh mesh) { List <BufferMesh> result = new(mesh.Primitives.Count); IColorConverter <LinearRGBColor, RGBColor> converter = new ConverterBuilder().FromLinearRGB().ToRGB(RGBWorkingSpaces.sRGB).Build(); foreach (var primitive in mesh.Primitives) { // read vertices primitive.VertexAccessors.TryGetValue("POSITION", out Accessor positions); primitive.VertexAccessors.TryGetValue("NORMAL", out Accessor normals); var positionArray = positions.AsVector3Array(); var normalArray = normals?.AsVector3Array(); BufferVertex[] vertices = new BufferVertex[positionArray.Count]; for (int i = 0; i < vertices.Length; i++) { var pos = positionArray[i]; var nrm = normalArray?[i] ?? Vector3.UnitY; vertices[i] = new(pos, nrm, (ushort)i); } // read corners primitive.VertexAccessors.TryGetValue("TEXCOORD_0", out Accessor uvs); primitive.VertexAccessors.TryGetValue("COLOR_0", out Accessor colors); var uvArray = uvs?.AsVector2Array(); var colorArray = colors?.AsColorArray(); BufferCorner[] corners = new BufferCorner[vertices.Length]; for (int i = 0; i < corners.Length; i++) { Vector2 uv = uvArray?[i] ?? default; Vector4 col = colorArray?[i] ?? Vector4.UnitW; var linearCol = converter.Convert(new(col.X, col.Y, col.Z)); corners[i] = new((ushort)i, new((float)linearCol.R, (float)linearCol.G, (float)linearCol.B, col.W), uv); } // Read indices uint[] indices = GetIndices(primitive.IndexAccessor, positions.Count, primitive.DrawPrimitiveType); // convert material result.Add(new(vertices, false, corners, indices, GetMaterial(primitive.Material))); } Attach atc = new(result.ToArray()); atc.Name = mesh.Name; return(atc); }
/// <summary> /// Converts a buffer model to the GC format /// </summary> /// <param name="model">The model to converter</param> /// <param name="optimize">Whether to optimize the attaches</param> /// <param name="ignoreWeights">If conversion should still happen, despite weights existing</param> /// <param name="forceUpdate">Whether to convert, regardless of whether the attaches are already GC</param> public static void ConvertModelToGC(NJObject model, bool optimize = true, bool ignoreWeights = false, bool forceUpdate = false) { if (model.Parent != null) { throw new FormatException($"Model {model.Name} is not hierarchy root!"); } if (model.AttachFormat == AttachFormat.GC && !forceUpdate) { return; } if (model.HasWeight && !ignoreWeights) { throw new FormatException("Model is weighted, cannot convert to basic format!"); } AttachHelper.ProcessWeightlessModel(model, (cacheAtc, ogAtc) => { // getting the vertex information Vector3[] positions = new Vector3[cacheAtc.vertices.Length]; Vector3[] normals = new Vector3[positions.Length]; for (int i = 0; i < positions.Length; i++) { var vtx = cacheAtc.vertices[i]; positions[i] = vtx.Position; normals[i] = vtx.Normal; } // getting the corner information int cornerCount = 0; for (int i = 0; i < cacheAtc.corners.Length; i++) { cornerCount += cacheAtc.corners[i].Length; } Vector2[] texcoords = new Vector2[cornerCount]; Color[] colors = new Color[cornerCount]; Corner[][] corners = new Corner[cacheAtc.corners.Length][]; ushort cornerIndex = 0; for (int i = 0; i < corners.Length; i++) { BufferCorner[] bufferCorners = cacheAtc.corners[i]; Corner[] meshCorners = new Corner[bufferCorners.Length]; for (int j = 0; j < bufferCorners.Length; j++) { BufferCorner bcorner = bufferCorners[j]; texcoords[cornerIndex] = bcorner.Texcoord; colors[cornerIndex] = bcorner.Color; meshCorners[j] = new Corner() { PositionIndex = bcorner.VertexIndex, NormalIndex = bcorner.VertexIndex, UV0Index = cornerIndex, Color0Index = cornerIndex }; cornerIndex++; } corners[i] = meshCorners; } bool hasUVs = texcoords.Any(x => x != default); // if it has no normals, always use colors (even if they are all white) bool hasColors = colors.Any(x => x != Color.White) || !normals.Any(x => x != Vector3.UnitY); // Puttin together the vertex sets VertexSet[] vertexData = new VertexSet[2 + (hasUVs ? 1 : 0)]; IndexAttributeParameter iaParam = new() { IndexAttributes = IndexAttributes.HasPosition }; if (positions.Length > 256) { iaParam.IndexAttributes |= IndexAttributes.Position16BitIndex; } vertexData[0] = new VertexSet(positions, false); if (hasColors) { iaParam.IndexAttributes |= IndexAttributes.HasColor; if (colors.Length > 256) { iaParam.IndexAttributes |= IndexAttributes.Color16BitIndex; } vertexData[1] = new VertexSet(colors); } else { iaParam.IndexAttributes |= IndexAttributes.HasNormal; if (normals.Length > 256) { iaParam.IndexAttributes |= IndexAttributes.Normal16BitIndex; } vertexData[1] = new VertexSet(normals, true); } if (hasUVs) { iaParam.IndexAttributes |= IndexAttributes.HasUV; if (texcoords.Length > 256) { iaParam.IndexAttributes |= IndexAttributes.UV16BitIndex; } vertexData[2] = new VertexSet(texcoords); } // stitching polygons together BufferMaterial currentMaterial = null; Mesh ProcessBufferMesh(int index) { // generating parameter info List <IParameter> parameters = new(); BufferMaterial cacheMaterial = cacheAtc.materials[index]; if (currentMaterial == null) { parameters.Add(new VtxAttrFmtParameter(VertexAttribute.Position)); parameters.Add(new VtxAttrFmtParameter(hasColors ? VertexAttribute.Color0 : VertexAttribute.Normal)); if (hasUVs) { parameters.Add(new VtxAttrFmtParameter(VertexAttribute.Tex0)); } parameters.Add(iaParam); if (cacheMaterial == null) { currentMaterial = new BufferMaterial() { MaterialAttributes = MaterialAttributes.noSpecular }; } else { currentMaterial = cacheMaterial; } parameters.Add(new LightingParameter() { LightingAttributes = LightingParameter.DefaultLighting.LightingAttributes, ShadowStencil = currentMaterial.ShadowStencil }); parameters.Add(new BlendAlphaParameter() { SourceAlpha = currentMaterial.SourceBlendMode, DestAlpha = currentMaterial.DestinationBlendmode }); parameters.Add(new AmbientColorParameter() { AmbientColor = currentMaterial.Ambient }); TextureParameter texParam = new(); texParam.TextureID = (ushort)currentMaterial.TextureIndex; if (!currentMaterial.ClampU) { texParam.Tiling |= GCTileMode.RepeatU; } if (!currentMaterial.ClampV) { texParam.Tiling |= GCTileMode.RepeatV; } if (currentMaterial.MirrorU) { texParam.Tiling |= GCTileMode.MirrorU; } if (currentMaterial.MirrorV) { texParam.Tiling |= GCTileMode.MirrorV; } parameters.Add(texParam); parameters.Add(Unknown9Parameter.DefaultValues); parameters.Add(new TexCoordGenParameter() { TexCoordID = currentMaterial.TexCoordID, TexGenType = currentMaterial.TexGenType, TexGenSrc = currentMaterial.TexGenSrc, MatrixID = currentMaterial.MatrixID }); } else { if (currentMaterial.ShadowStencil != cacheMaterial.ShadowStencil) { parameters.Add(new LightingParameter() { ShadowStencil = cacheMaterial.ShadowStencil }); } if (currentMaterial.SourceBlendMode != cacheMaterial.SourceBlendMode || currentMaterial.DestinationBlendmode != cacheMaterial.DestinationBlendmode) { parameters.Add(new BlendAlphaParameter() { SourceAlpha = cacheMaterial.SourceBlendMode, DestAlpha = cacheMaterial.DestinationBlendmode }); } if (currentMaterial.Ambient != cacheMaterial.Ambient) { parameters.Add(new AmbientColorParameter() { AmbientColor = cacheMaterial.Ambient }); } if (currentMaterial.TextureIndex != cacheMaterial.TextureIndex || currentMaterial.MirrorU != cacheMaterial.MirrorU || currentMaterial.MirrorV != cacheMaterial.MirrorV || currentMaterial.ClampU != cacheMaterial.ClampU || currentMaterial.ClampV != cacheMaterial.ClampV) { TextureParameter texParam = new(); texParam.TextureID = (ushort)cacheMaterial.TextureIndex; if (!cacheMaterial.ClampU) { texParam.Tiling |= GCTileMode.RepeatU; } if (!cacheMaterial.ClampV) { texParam.Tiling |= GCTileMode.RepeatV; } if (cacheMaterial.MirrorU) { texParam.Tiling |= GCTileMode.MirrorU; } if (cacheMaterial.MirrorV) { texParam.Tiling |= GCTileMode.MirrorV; } parameters.Add(texParam); } if (currentMaterial.TexCoordID != cacheMaterial.TexCoordID || currentMaterial.TexGenType != cacheMaterial.TexGenType || currentMaterial.TexGenSrc != cacheMaterial.TexGenSrc || currentMaterial.MatrixID != cacheMaterial.MatrixID) { parameters.Add(new TexCoordGenParameter() { TexCoordID = cacheMaterial.TexCoordID, TexGenType = cacheMaterial.TexGenType, TexGenSrc = cacheMaterial.HasAttribute(MaterialAttributes.normalMapping) ? TexGenSrc.Normal : cacheMaterial.TexGenSrc, MatrixID = cacheMaterial.MatrixID }); } currentMaterial = cacheMaterial; } // note: a single triangle polygon can only carry 0xFFFF corners, so about 22k tris Corner[] triangleCorners = corners[index]; List <Poly> polygons = new(); if (triangleCorners.Length > 0xFFFF) { int remainingLength = triangleCorners.Length; int offset = 0; while (remainingLength > 0) { Corner[] finalCorners = new Corner[Math.Max(0xFFFF, remainingLength)]; Array.Copy(triangleCorners, offset, finalCorners, 0, finalCorners.Length); offset += finalCorners.Length; remainingLength -= finalCorners.Length; Poly triangle = new(PolyType.Triangles, finalCorners); polygons.Add(triangle); }
private static (AttachHelper.VertexWeights[] vertices, BufferMesh[] polydata) FromWeight(Mesh mesh) { List <AttachHelper.VertexWeights> vertices = new(); List <int> offsets = new(); int offset = 0; foreach (var primitive in mesh.Primitives) { // read vertices primitive.VertexAccessors.TryGetValue("POSITION", out Accessor positions); primitive.VertexAccessors.TryGetValue("NORMAL", out Accessor normals); primitive.VertexAccessors.TryGetValue("WEIGHTS_0", out Accessor weights); primitive.VertexAccessors.TryGetValue("JOINTS_0", out Accessor joints); var positionArray = positions.AsVector3Array(); var normalArray = normals?.AsVector3Array(); var weightsArray = weights.AsVector4Array(); var jointsArray = joints.AsVector4Array(); int vertCount = positionArray.Count; for (int i = 0; i < vertCount; i++) { // position var pos = positionArray[i]; // normal (may be null) var nrm = normalArray?[i] ?? Vector3.UnitY; var joint = jointsArray[i]; var weight = weightsArray[i]; List <(int, float)> skinning = new(); if (weight.X > 0) { skinning.Add(((int)joint.X, weight.X)); } if (weight.Y > 0) { skinning.Add(((int)joint.Y, weight.Y)); } if (weight.Z > 0) { skinning.Add(((int)joint.Z, weight.Z)); } if (weight.W > 0) { skinning.Add(((int)joint.W, weight.W)); } vertices.Add(new(pos, nrm, skinning.ToArray())); } offsets.Add(offset); offset += positionArray.Count; } // first, lets put the poly meshes together BufferMesh[] polyMeshes = new BufferMesh[mesh.Primitives.Count]; int offsetIndex = 0; foreach (var primitive in mesh.Primitives) { // getting positions again to get corner count var positions = primitive.VertexAccessors["POSITION"]; // read corners primitive.VertexAccessors.TryGetValue("TEXCOORD_0", out Accessor uvs); primitive.VertexAccessors.TryGetValue("COLOR_0", out Accessor colors); var uvArray = uvs?.AsVector2Array(); var colorArray = colors?.AsColorArray(); BufferCorner[] corners = new BufferCorner[positions.Count]; for (int i = 0; i < corners.Length; i++) { Vector2 uv = uvArray?[i] ?? default; Vector4 col = colorArray?[i] ?? Vector4.UnitW; corners[i] = new((ushort)i, new(col.X, col.Y, col.Z, col.W), uv); } // Read indices uint[] indices = GetIndices(primitive.IndexAccessor, positions.Count, primitive.DrawPrimitiveType); ushort vertexReadOffset = (ushort)offsets[offsetIndex]; polyMeshes[offsetIndex] = new(corners, indices, GetMaterial(primitive.Material), vertexReadOffset); offsetIndex++; } return(vertices.ToArray(), polyMeshes); }
/// <summary> /// Converts the buffer data of a model to BASIC attaches /// </summary> /// <param name="model">The tip of the model hierarchy to convert</param> /// <param name="optimize">Whether to optimize the data</param> /// <param name="ignoreWeights">Convert regardless of weight information being lost</param> /// <param name="forceUpdate">Still convert, even if the attaches are Basic already</param> public static void ConvertModelToBasic(NJObject model, bool optimize = true, bool ignoreWeights = false, bool forceUpdate = false) { if (model.Parent != null) { throw new FormatException($"Model {model.Name} is not hierarchy root!"); } if (model.AttachFormat == AttachFormat.BASIC && !forceUpdate) { return; } if (model.HasWeight && !ignoreWeights) { throw new FormatException("Model is weighted, cannot convert to basic format!"); } AttachHelper.ProcessWeightlessModel(model, (cacheAtc, ogAtc) => { // getting the vertex information Vector3[] positions = new Vector3[cacheAtc.vertices.Length]; Vector3[] normals = new Vector3[positions.Length]; bool hasNormals = false; for (int i = 0; i < positions.Length; i++) { var vtx = cacheAtc.vertices[i]; positions[i] = vtx.Position; normals[i] = vtx.Normal; if (vtx.Normal != Vector3.UnitY) { hasNormals = true; } } if (!hasNormals) { normals = null; } // putting together polygons Mesh[] meshes = new Mesh[cacheAtc.corners.Length]; Material[] materials = new Material[cacheAtc.corners.Length]; for (int i = 0; i < cacheAtc.corners.Length; i++) { // creating the material Material mat = new(); BufferMaterial bmat = cacheAtc.materials[i]; if (bmat != null) { mat.DiffuseColor = bmat.Diffuse; mat.SpecularColor = bmat.Specular; mat.Exponent = bmat.SpecularExponent; mat.TextureID = bmat.TextureIndex; mat.FilterMode = bmat.TextureFiltering; mat.MipmapDAdjust = bmat.MipmapDistanceAdjust; mat.SuperSample = bmat.AnisotropicFiltering; mat.ClampU = bmat.ClampU; mat.ClampV = bmat.ClampV; mat.MirrorU = bmat.MirrorU; mat.MirrorV = bmat.MirrorV; mat.UseAlpha = bmat.UseAlpha; mat.SourceAlpha = bmat.SourceBlendMode; mat.DestinationAlpha = bmat.DestinationBlendmode; mat.DoubleSided = !bmat.Culling; mat.IgnoreLighting = bmat.HasAttribute(MaterialAttributes.noDiffuse); mat.IgnoreSpecular = bmat.HasAttribute(MaterialAttributes.noSpecular); mat.UseTexture = bmat.HasAttribute(MaterialAttributes.useTexture); mat.EnvironmentMap = bmat.HasAttribute(MaterialAttributes.normalMapping); } materials[i] = mat; // creating the polygons BufferCorner[] bCorners = cacheAtc.corners[i]; IPoly[] triangles = new IPoly[bCorners.Length / 3]; Vector2[] texcoords = new Vector2[bCorners.Length]; Color[] colors = new Color[bCorners.Length]; Triangle current = new(); for (int j = 0; j < bCorners.Length; j++) { BufferCorner corner = bCorners[j]; int vIndex = j % 3; current.Indices[vIndex] = corner.VertexIndex; if (vIndex == 2) { triangles[(j - 2) / 3] = current; current = new Triangle(); } texcoords[j] = corner.Texcoord; colors[j] = corner.Color; } bool hasTexcoords = texcoords.Any(x => x != default); bool hasColors = colors.Any(x => x != Color.White); Mesh basicmesh = new (BASICPolyType.Triangles, triangles, false, hasColors, hasTexcoords, (ushort)i); if (hasColors) { basicmesh.Colors = colors; } if (hasTexcoords) { basicmesh.Texcoords = texcoords; } meshes[i] = basicmesh; }