public Picture(BinaryReaderX br) : base(br) { vertexColorLT = new Color4(br); vertexColorRT = new Color4(br); vertexColorLB = new Color4(br); vertexColorRB = new Color4(br); matID = br.ReadUInt16(); texCoordEntryCount = br.ReadUInt16(); TexCoordEntries = new TexCoord[texCoordEntryCount]; for (int i = 0; i < texCoordEntryCount; i++) { TexCoordEntries[i] = new TexCoord(br); } }
public WindowContent(EndianBinaryReader er) { VertexColorLT = er.ReadColor8(); VertexColorRT = er.ReadColor8(); VertexColorLB = er.ReadColor8(); VertexColorRB = er.ReadColor8(); MaterialId = er.ReadUInt16(); NrTexCoordEntries = er.ReadUInt16(); TexCoordEntries = new TexCoord[NrTexCoordEntries]; for (int i = 0; i < NrTexCoordEntries; i++) { TexCoordEntries[i] = new TexCoord(er); } }
public pic1(EndianBinaryReader er) : base(er) { VertexColorLT = er.ReadColor8(); VertexColorRT = er.ReadColor8(); VertexColorLB = er.ReadColor8(); VertexColorRB = er.ReadColor8(); MaterialId = er.ReadUInt16(); NrTexCoordEntries = er.ReadUInt16(); TexCoordEntries = new TexCoord[NrTexCoordEntries]; for (int i = 0; i < NrTexCoordEntries; i++) { TexCoordEntries[i] = new TexCoord(er); } }
public PIC1(Header header, string name) : base() { LoadDefaults(); Name = name; ParentLayout = header; ColorTopLeft = STColor8.White; ColorTopRight = STColor8.White; ColorBottomLeft = STColor8.White; ColorBottomRight = STColor8.White; TexCoords = new TexCoord[1]; TexCoords[0] = new TexCoord(); Material = new Material(name, header); }
public BSEffectShaderProperty() { shaderFlags1_sk = (SkyrimShaderPropertyFlags1)2147483648; shaderFlags2_sk = (SkyrimShaderPropertyFlags2)32; shaderFlags1_fo4 = (Fallout4ShaderPropertyFlags1)2147483648; shaderFlags2_fo4 = (Fallout4ShaderPropertyFlags2)32; uvScale = 1.0, 1.0; textureClampMode = (byte)0; lightingInfluence = (byte)0; envMapMinLod = (byte)0; unknownByte = (byte)0; falloffStartAngle = 1.0f; falloffStopAngle = 1.0f; falloffStartOpacity = 0.0f; falloffStopOpacity = 0.0f; emissiveMultiple = 0.0f; softFalloffDepth = 0.0f; environmentMapScale = 0.0f; }
internal override void Process(ParsingContext context) { var reader = context.reader; var chunk = this; int cnt = reader.ReadUInt16(); chunk.BytesRead += 2; Console.WriteLine(" TexCoords: {0}", cnt); var texCoords = new TexCoord[cnt]; for (int ii = 0; ii < cnt; ii++) { texCoords[ii] = new TexCoord(reader.ReadSingle(), reader.ReadSingle()); } chunk.BytesRead += (uint)(cnt * (4 * 2)); this.texCoords = texCoords; }
public bool Equals(Vertex vertex, bool checkBones) { if (checkBones) { if (BoneWeights.Count != vertex.BoneWeights.Count) { return(false); } if (!BoneWeights.SequenceEqual(vertex.BoneWeights)) { return(false); } } return(Position.Equals(vertex.Position) && Normal.Equals(vertex.Normal) && TexCoord.Equals(vertex.TexCoord)); }
//Constructor public TexDesc() { unchecked { image = null; source = null; clampMode = TexClampMode.WRAP_S_WRAP_T; filterMode = TexFilterMode.FILTER_TRILERP; flags = (ushort)0; maxAnisotropy = (ushort)0; uvSet = (uint)0; ps2L = (short)0; ps2K = (short)-75; unknown1 = (ushort)0; hasTextureTransform = false; scale = 1.0, 1.0; rotation = 0.0f; transformMethod = (TransformMethod)0; } }
public PIC1(FileReader reader, BLOHeader header) : base(reader, header) { byte numParams = reader.ReadByte(); if (numParams > 0) { TextureName = BloResource.Read(reader, header); numParams--; } if (numParams > 0) { PaletteName = BloResource.Read(reader, header); numParams--; } if (numParams > 0) { Binding = reader.ReadByte(); numParams--; } Material = new Material(); if (TextureName == string.Empty) { Material.TextureMaps = new BxlytTextureRef[0]; } else { Material.TextureMaps = new BxlytTextureRef[1]; Material.TextureMaps[0] = new BxlytTextureRef() { Name = TextureName, }; } TexCoords = new TexCoord[1]; TexCoords[0] = new TexCoord(); }
public BSLightingShaderProperty() { shaderFlags1_sk = (SkyrimShaderPropertyFlags1)2185233153; shaderFlags2_sk = (SkyrimShaderPropertyFlags2)32801; shaderFlags1_fo4 = (Fallout4ShaderPropertyFlags1)2151678465; shaderFlags2_fo4 = (Fallout4ShaderPropertyFlags2)1; uvScale = (1.0, 1.0); textureSet = null; emissiveColor = (0.0, 0.0, 0.0); emissiveMultiple = 0.0f; textureClampMode = (TexClampMode)3; alpha = 1.0f; refractionStrength = 0.0f; glossiness = 80f; smoothness = 1.0f; specularStrength = 1.0f; lightingEffect1 = 0.3f; lightingEffect2 = 2.0f; subsurfaceRolloff = 0.3f; rimlightPower = 3.402823466e+38f; backlightPower = 0.0f; grayscaleToPaletteScale = 0.0f; fresnelPower = 5.0f; wetnessSpecScale = -1.0f; wetnessSpecPower = -1.0f; wetnessMinVar = -1.0f; wetnessEnvMapScale = -1.0f; wetnessFresnelPower = -1.0f; wetnessMetalness = -1.0f; environmentMapScale = 1.0f; unknownEnvMapShort = (ushort)0; unknownSkinTintInt = (uint)0; maxPasses = 0.0f; scale = 0.0f; parallaxInnerLayerThickness = 0.0f; parallaxRefractionScale = 0.0f; parallaxEnvmapStrength = 0.0f; eyeCubemapScale = 0.0f; }
public bool Equals(Vertex other) { const float precision = 0.00001f; if (other == null) { return(false); } if (Influence != other.Influence) { return(false); } if (!Position.Equals(other.Position, precision)) { return(false); } if (!Normal.Equals(other.Normal, precision)) { return(false); } if (!Binormal.Equals(other.Binormal, precision)) { return(false); } if (!Tangent.Equals(other.Tangent, precision)) { return(false); } if (!Color.Equals(other.Color, precision)) { return(false); } if (!TexCoord.Equals(other.TexCoord, precision)) { return(false); } return(true); }
public PIC2(FileReader reader, BLOHeader header) : base(reader, header) { TexCoords = new TexCoord[1]; TexCoords[0] = new TexCoord(); ushort sectionSize = reader.ReadUInt16(); Console.WriteLine($"PIC2 {sectionSize}"); MaterialIndex = reader.ReadUInt16(); Material = header.Materials[MaterialIndex]; uint unk = reader.ReadUInt32(); //These increase for each material ushort index1 = reader.ReadUInt16(); ushort index2 = reader.ReadUInt16(); ushort index3 = reader.ReadUInt16(); ushort index4 = reader.ReadUInt16(); //I think these are texture coordinates TexCoords[0].TopLeft = new Vector2F( reader.ReadInt16() / 256f, reader.ReadInt16() / 256f); TexCoords[0].TopRight = new Vector2F( reader.ReadInt16() / 256f, reader.ReadInt16() / 256f); TexCoords[0].BottomLeft = new Vector2F( reader.ReadInt16() / 256f, reader.ReadInt16() / 256f); TexCoords[0].BottomRight = new Vector2F( reader.ReadInt16() / 256f, reader.ReadInt16() / 256f); ColorTopLeft = STColor8.FromBytes(reader.ReadBytes(4)); ColorTopRight = STColor8.FromBytes(reader.ReadBytes(4)); ColorBottomLeft = STColor8.FromBytes(reader.ReadBytes(4)); ColorBottomRight = STColor8.FromBytes(reader.ReadBytes(4)); }
public void Read(Reader reader) { offset = Pointer.Current(reader); if (geo.Type == 4 || geo.Type == 5 || geo.Type == 6) { // Optimized num_vertices_actual = reader.ReadUInt32(); num_vertices = reader.ReadUInt32(); num_uvs = reader.ReadUInt32(); unk3 = reader.ReadUInt32(); } else { num_vertices = geo.num_triangles[index] * 3; num_uvs = (num_vertices + 3) >> 2; num_vertices_actual = num_vertices; } VisualMaterial vm = geo.visualMaterials[index]; bool hasUv0 = true, hasUv1 = false, hasNormals = false; uint num_textures = 1; if (vm != null && vm.num_textures_in_material > 0) { hasUv0 = false; num_textures = vm.num_textures_in_material; for (int i = 0; i < vm.num_textures_in_material; i++) { switch (vm.textures[i].uvFunction) { case 4: hasNormals = true; break; case 1: hasUv1 = true; break; case 0: hasUv0 = true; break; } } } vertices = new Vertex[num_vertices]; for (int i = 0; i < num_vertices; i++) { vertices[i] = new Vertex(reader); } if (geo.Type == 1 && Settings.s.game == Settings.Game.R3) { uvUnoptimized = new UVUnoptimized[num_vertices]; for (int i = 0; i < uvUnoptimized.Length; i++) { uvUnoptimized[i] = new UVUnoptimized(reader); } } else { if (hasUv0) { uv0 = new TexCoord[num_uvs]; for (int i = 0; i < uv0.Length; i++) { uv0[i] = new TexCoord(reader); } } if (hasUv1) { uv1 = new TexCoord[num_uvs]; for (int i = 0; i < uv1.Length; i++) { uv1[i] = new TexCoord(reader); } } if (hasNormals) { normals = new Normal[num_vertices]; for (int i = 0; i < normals.Length; i++) { normals[i] = new Normal(reader); } } if ((geo.flags & 0x100) != 0) { uv_unk = new TexCoord[num_uvs]; for (int i = 0; i < uv_unk.Length; i++) { uv_unk[i] = new TexCoord(reader); } } } colors = new VertexColor[num_textures][]; // Seem to be in a color-like format? 7F 7F 7F 80, repeated 4 times for (int i = 0; i < colors.Length; i++) { colors[i] = new VertexColor[num_uvs]; for (int j = 0; j < colors[i].Length; j++) { colors[i][j] = new VertexColor(reader); } } if (geo.isSinus != 0) { sinusState = new VectorForSinusEffect[num_uvs]; for (int i = 0; i < sinusState.Length; i++) { sinusState[i] = new VectorForSinusEffect(reader); } } if ((index < geo.num_elements - 1 && Pointer.Current(reader) != geo.off_elements_array[index + 1]) || (index == geo.num_elements - 1 && Pointer.Current(reader) != geo.off_uint1)) { UnityEngine.Debug.LogWarning("B " + geo.Offset + " - " + offset + " - " + hasUv0 + " - " + hasUv1 + " - " + hasNormals); } else { //UnityEngine.Debug.LogWarning("G " + geo.Offset + " - " + offset + " - " + hasUv0 + " - " + hasUv1 + " - " + hasUv4); } /*normals = new Vertex[num_vertices]; * for (int i = 0; i < num_vertices; i++) { * normals[i] = new Vertex(reader); * }*/ }
protected bool Equals(TexCoord other) { return(MinU.Equals(other.MinU) && MinV.Equals(other.MinV) && MaxU.Equals(other.MaxU) && MaxV.Equals(other.MaxV)); }
//public override bool Equals(object obj) //{ // return obj is Vertex vertex && // Position.Equals(vertex.Position) && // Normal.Equals(vertex.Normal) && // TexCoord.Equals(vertex.TexCoord); //} public bool Equals(Vertex vertex) { return(Position.Equals(vertex.Position) && Normal.Equals(vertex.Normal) && TexCoord.Equals(vertex.TexCoord)); }
public static void NifStream(TexCoord val, OStream s, NifInfo info) { WriteFloat(val.u, s); WriteFloat(val.v, s); }
public static void Read(string fileName, ConversionSettings conversionSettings, ref DisplayList dsp, out Dictionary <string, TextureInfo> allMaterials, out string[] messages) { messages = new string[0]; if (conversionSettings.colorInterpretation == ConversionSettings.ColorInterpretation.Undefined) { if (!conversionSettings.DoColorInterpretationDialog()) { allMaterials = null; return; } } string rootDirectory = System.IO.Path.GetDirectoryName(fileName); System.Xml.XmlDataDocument doc = new XmlDataDocument(); doc.Load(fileName); XmlNamespaceManager mgr = new XmlNamespaceManager(doc.NameTable); mgr.AddNamespace("df", doc.DocumentElement.NamespaceURI); List <Subset> meshes = new List <Subset>(); XmlNode imageLibraryNode = doc.SelectSingleNode("//df:COLLADA/df:library_images", mgr); XmlNode materialLibraryNode = doc.SelectSingleNode("//df:COLLADA/df:library_materials", mgr); XmlNode effectLibraryNode = doc.SelectSingleNode("//df:COLLADA/df:library_effects", mgr); XmlNode geometryLibraryNode = doc.SelectSingleNode("//df:COLLADA/df:library_geometries", mgr); XmlNode sceneLibraryNode = doc.SelectSingleNode("//df:COLLADA/df:library_visual_scenes", mgr); XmlNodeList geometryNodes = sceneLibraryNode.SelectNodes("df:visual_scene/df:node/df:instance_geometry", mgr); bool flipYZ = doc.SelectSingleNode("//df:COLLADA/df:asset/df:up_axis", mgr).InnerText == "Z_UP"; allMaterials = new Dictionary <string, TextureInfo>(); allMaterials["<Undefined>"] = new TextureInfo(null); foreach (XmlNode geometryNode in geometryNodes) { XmlNode meshNode = geometryLibraryNode.SelectSingleNode("df:geometry[@id='" + geometryNode.Attributes["url"].Value.Remove(0, 1) + "']/df:mesh", mgr); XmlNode transformNode = geometryNode.SelectSingleNode("../df:matrix[@sid='transform']", mgr); Matrix transform = Matrix.identity; if (transformNode != null) { transform.m = Array.ConvertAll <string, float>(transformNode.InnerText.Split(' '), (s) => float.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture)); } Matrix normalTransform = transform.InvertTranspose(); foreach (XmlNode trianglesNode in meshNode.SelectNodes("df:triangles", mgr)) { Subset meshSubset = new Subset(); meshes.Add(meshSubset); List <Vertex> vertexBuffer = new List <Vertex>(); //final vertex buffer for this material List <int> indexBuffer = new List <int>(); //final index buffer for this material //Data to be read from XML XmlAttribute materialAttribute = trianglesNode.Attributes["material"]; if (materialAttribute == null) { messages = new string[] { "Mesh without materials found. Skipping..." }; continue; } string material = materialAttribute.Value; string effect = materialLibraryNode.SelectSingleNode("df:material[@id='" + material + "']/df:instance_effect", mgr).Attributes["url"].Value.Remove(0, 1); XmlNode effectNode = effectLibraryNode.SelectSingleNode("df:effect[@id='" + effect + "']/df:profile_COMMON", mgr); XmlNode textureNode = effectNode.SelectSingleNode("df:technique/*/df:diffuse/df:texture", mgr); if (textureNode == null) { meshSubset.Texture = allMaterials["<Undefined>"]; } else { string surfaceName = effectNode.SelectSingleNode("df:newparam[@sid='" + textureNode.Attributes["texture"].Value + "']/df:sampler2D/df:source", mgr).InnerText; string init_from = effectNode.SelectSingleNode("df:newparam[@sid='" + surfaceName + "']/df:surface/df:init_from", mgr).InnerText; string imageFileName = imageLibraryNode.SelectSingleNode("df:image[@id='" + init_from + "']/df:init_from", mgr).InnerText; string fullPath = imageFileName; if (imageFileName[1] != ':') //Path is not absolute. Make Absolute { fullPath = System.IO.Path.Combine(rootDirectory, imageFileName); } fullPath = Uri.UnescapeDataString(fullPath); if (!allMaterials.TryGetValue(imageFileName, out meshSubset.Texture)) { meshSubset.Texture = allMaterials[imageFileName] = new TextureInfo(fullPath); } } int vertexOffset, normalOffset, texCoordOffset, colorOffset; vertexOffset = normalOffset = texCoordOffset = colorOffset = -1; Vec3[] positions = null, normals = null; TexCoord[] texCoords = null; Color[] colors = null; XmlNode vertexNode = trianglesNode.SelectSingleNode("df:input[@semantic='VERTEX']", mgr); if (vertexNode != null) { XmlNode vertexSource = meshNode.SelectSingleNode("df:vertices[@id='" + vertexNode.Attributes["source"].Value.Remove(0, 1) + "']", mgr); XmlNode vertexSourceSource /*jeez pls give me vertex positions now*/ = meshNode.SelectSingleNode("df:source[@id='" + vertexSource.SelectSingleNode("df:input[@semantic='POSITION']", mgr).Attributes["source"].Value.Remove(0, 1) + "']", mgr); positions = ReadXMLVectorNode <Vec3>(vertexSourceSource, mgr); vertexOffset = int.Parse(vertexNode.Attributes["offset"].Value); } XmlNode normalNode = trianglesNode.SelectSingleNode("df:input[@semantic='NORMAL']", mgr); if (normalNode != null) { XmlNode normalSource = meshNode.SelectSingleNode("df:source[@id='" + normalNode.Attributes["source"].Value.Remove(0, 1) + "']", mgr); normals = ReadXMLVectorNode <Vec3>(normalSource, mgr); normalOffset = int.Parse(normalNode.Attributes["offset"].Value); } XmlNode texCoordNode = trianglesNode.SelectSingleNode("df:input[@semantic='TEXCOORD']", mgr); if (texCoordNode != null) { XmlNode texCoordSource = meshNode.SelectSingleNode("df:source[@id='" + texCoordNode.Attributes["source"].Value.Remove(0, 1) + "']", mgr); texCoords = ReadXMLVectorNode <TexCoord>(texCoordSource, mgr); texCoordOffset = int.Parse(texCoordNode.Attributes["offset"].Value); } XmlNode colorNode = trianglesNode.SelectSingleNode("df:input[@semantic='COLOR']", mgr); if (colorNode != null) { XmlNode colorSource = meshNode.SelectSingleNode("df:source[@id='" + colorNode.Attributes["source"].Value.Remove(0, 1) + "']", mgr); colors = ReadXMLVectorNode <Color>(colorSource, mgr); colorOffset = int.Parse(colorNode.Attributes["offset"].Value); } int[] indices = Array.ConvertAll(trianglesNode.SelectSingleNode("df:p", mgr).InnerText.Split(' '), (string s) => int.Parse(s)); Vertex[] vertices = new Vertex[int.Parse(trianglesNode.Attributes["count"].Value) * 3]; int stride = indices.Length / vertices.Length; int currentIndex = 0; Color defaultColor = new Color(); defaultColor.R = defaultColor.G = defaultColor.B = defaultColor.A = 1; for (int i = 0; i < vertices.Length; i++) { Vec3 position = positions == null ? new Vec3() : positions[indices[i * stride + vertexOffset]]; position = position.TransformPosition(transform); Vec3 normal = normals == null ? new Vec3() : normals[indices[i * stride + normalOffset]]; normal = normal.TransformNormal(normalTransform); TexCoord texCoord = texCoords == null ? new TexCoord() : texCoords[indices[i * stride + texCoordOffset]]; Color color = colors == null ? defaultColor : colors[indices[i * stride + colorOffset]]; vertices[i] = flipYZ ? new Vertex(new Vector3(position.X, position.Z, -position.Y), new Vector2(texCoord.S, texCoord.T), new Vector3(normal.X, normal.Z, -normal.Y)) : new Vertex(new Vector3(position.X, position.Y, position.Z), new Vector2(texCoord.S, texCoord.T), new Vector3(normal.X, normal.Y, normal.Z)); Color c = colors == null ? defaultColor : colors[indices[i * stride + 3]]; if (conversionSettings.colorInterpretation == ConversionSettings.ColorInterpretation.ReplaceNormal) { vertices[i].nx = (sbyte)(c.R * 255); vertices[i].ny = (sbyte)(c.G * 255); vertices[i].nz = (sbyte)(c.B * 255); vertices[i].c = 255; } else if (conversionSettings.colorInterpretation == ConversionSettings.ColorInterpretation.ConvertRedToAlpha) { vertices[i].c = (byte)(c.R * 255); } else if (conversionSettings.colorInterpretation == ConversionSettings.ColorInterpretation.ConvertGreenToAlpha) { vertices[i].c = (byte)(c.G * 255); } else if (conversionSettings.colorInterpretation == ConversionSettings.ColorInterpretation.ConvertBlueToAlpha) { vertices[i].c = (byte)(c.B * 255); } else if (conversionSettings.colorInterpretation == ConversionSettings.ColorInterpretation.ConvertRedToAlpha) { vertices[i].c = (byte)((c.R + c.G + c.B) / 3 * 255); } int k = 0; foreach (Vertex v in vertexBuffer) { if (v.Equals(vertices[i])) { indexBuffer.Add(k); goto skipNewVertex; } k++; } vertexBuffer.Add(vertices[i]); indexBuffer.Add(currentIndex++); skipNewVertex :; } meshSubset.IndexBuffer = indexBuffer.ToArray(); meshSubset.VertexBuffer = vertexBuffer.ToArray(); meshSubset.CreatePatches(); } } dsp.subsets = meshes.ToArray(); }
/*! * Generate or update a NiStringExtraData object with precalculated * tangent and binormal data (Oblivion specific) * \param[in] method Calculation method. [0 - Nifskope; 1 - Obsidian] */ public void UpdateTangentSpace(int method = 0) { var niTriGeomData = data as NiTriBasedGeomData; /* No data, no tangent space */ if (niTriGeomData == null) { throw new Exception("There is no NiTriBasedGeomData attached the NiGeometry upon which UpdateTangentSpace was called."); } //Check if there are any UVs or Vertices before trying to retrive them if (niTriGeomData.UVSetCount == 0) { //There are no UVs, do nothing return; } if (niTriGeomData.VertexCount == 0) { //There are no Vertices, do nothing return; } //Get mesh data from data object var verts = niTriGeomData.Vertices; var norms = niTriGeomData.Normals; var tris = niTriGeomData.GetTriangles(); var uvs = niTriGeomData.GetUVSet(0); /* check for data validity */ if (verts.Count != norms.Count || verts.Count != uvs.Count || tris.empty()) { //Do nothing, there is no shape in this data. return; } var tangents = new Vector3[verts.Count]; var bitangents = new Vector3[verts.Count]; if (method == 0) // Nifskope algorithm { for (int t = 0; t < (int)tris.size(); t++) { var tri = tris[t]; var i1 = tri[0]; var i2 = tri[1]; var i3 = tri[2]; var v1 = verts[i1]; var v2 = verts[i2]; var v3 = verts[i3]; var w1 = uvs[i1]; var w2 = uvs[i2]; var w3 = uvs[i3]; var v2v1 = v2 - v1; var v3v1 = v3 - v1; var w2w1 = new TexCoord(w2.u - w1.u, w2.v - w1.v); var w3w1 = new TexCoord(w3.u - w1.u, w3.v - w1.v); var r = w2w1.u * w3w1.v - w3w1.u * w2w1.v; r = (r >= 0.0f ? +1.0f : -1.0f); var sdir = new Vector3( (w3w1.v * v2v1.x - w2w1.v * v3v1.x) * r, (w3w1.v * v2v1.y - w2w1.v * v3v1.y) * r, (w3w1.v * v2v1.z - w2w1.v * v3v1.z) * r); var tdir = new Vector3( (w2w1.u * v3v1.x - w3w1.u * v2v1.x) * r, (w2w1.u * v3v1.y - w3w1.u * v2v1.y) * r, (w2w1.u * v3v1.z - w3w1.u * v2v1.z) * r); sdir = sdir.Normalized(); tdir = tdir.Normalized(); // no duplication, just smoothing for (var j = 0; j < 3; j++) { var i = tri[j]; tangents[i] += tdir; bitangents[i] += sdir; } } // for each vertex calculate tangent and binormal for (var i = 0; i < verts.Count; i++) { var n = norms[i]; var t = tangents[i]; var b = bitangents[i]; if (t == new Vector3() || b == new Vector3()) { t.x = n.y; t.y = n.z; t.z = n.x; b = n.CrossProduct(t); } else { t = t.Normalized(); t = (t - n * n.DotProduct(t)); t = t.Normalized(); b = b.Normalized(); b = (b - n * n.DotProduct(b)); b = (b - t * t.DotProduct(b)); b = b.Normalized(); } } } else if (method == 1) // Obsidian Algorithm { for (var faceNo = 0; faceNo < tris.Count; ++faceNo) // for each face { var t = tris[faceNo]; // get face int i0 = t[0], i1 = t[1], i2 = t[2]; // get vertex numbers var side_0 = verts[i0] - verts[i1]; var side_1 = verts[i2] - verts[i1]; var delta_U_0 = uvs[i0].u - uvs[i1].u; var delta_U_1 = uvs[i2].u - uvs[i1].u; var delta_V_0 = uvs[i0].v - uvs[i1].v; var delta_V_1 = uvs[i2].v - uvs[i1].v; var face_tangent = (side_0 * delta_V_1 - side_1 * delta_V_0).Normalized(); var face_bi_tangent = (side_0 * delta_U_1 - side_1 * delta_U_0).Normalized(); var face_normal = (side_0 ^ side_1).Normalized(); // no duplication, just smoothing for (var j = 0; j <= 2; j++) { var i = t[j]; tangents[i] += face_tangent; bitangents[i] += face_bi_tangent; } } // for each.getPosition(), normalize the Tangent and Binormal for (var i = 0; i < verts.Count; i++) { bitangents[i] = bitangents[i].Normalized(); tangents[i] = tangents[i].Normalized(); } } if ((niTriGeomData.TspaceFlag & 0xF0) == 0) { // generate the byte data var vCount = verts.Count; var fSize = sizeof(float) * 3; var binData = new byte[2 * vCount * fSize]; for (var i = 0; i < verts.Count; i++) { float[] tan_xyz = new float[3], bin_xyz = new float[3]; tan_xyz[0] = tangents[i].x; tan_xyz[1] = tangents[i].y; tan_xyz[2] = tangents[i].z; bin_xyz[0] = bitangents[i].x; bin_xyz[1] = bitangents[i].y; bin_xyz[2] = bitangents[i].z; var tan_Bytes = BitConverter.GetBytes(tan_xyz); var bin_Bytes = BitConverter.GetBytes(bin_xyz); for (var j = 0; j < fSize; j++) { binData[i * fSize + j] = tan_Bytes[j]; binData[(i + vCount) * fSize + j] = bin_Bytes[j]; } } // update or create the tangent space extra data NiBinaryExtraData tspaceRef = null; var props = GetExtraData(); foreach (var prop in props) { if (prop.Name == "Tangent space (binormal & tangent vectors)") { tspaceRef = prop as NiBinaryExtraData; break; } } if (tspaceRef == null) { tspaceRef = new NiBinaryExtraData(); tspaceRef.Name = "Tangent space (binormal & tangent vectors)"; AddExtraData((NiExtraData)tspaceRef); } tspaceRef.Data = binData; } else { // swap bitangents and tangents: [ niftools-Bugs-2466995 ] niTriGeomData.Tangents = bitangents; niTriGeomData.Bitangents = tangents; } }