Add option to smooth normals between multiple meshes
This commit is contained in:
parent
d8dbefa3af
commit
04c43b8bd5
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -75,8 +75,9 @@ namespace Bfres.Structs
|
||||
ContextMenuStrip.Items.Add(new ToolStripMenuItem("Transform", null, TransformToolAction, Keys.Control | Keys.T));
|
||||
ContextMenuStrip.Items.Add(new ToolStripMenuItem("Calculate Tangents/Bitangents", null, CalcTansBitansAllShapesAction, Keys.Control | Keys.C));
|
||||
ContextMenuStrip.Items.Add(new ToolStripMenuItem("Normals", null,
|
||||
new ToolStripMenuItem("Smooth", null, SmoothNormalsAction),
|
||||
new ToolStripMenuItem("Recalculate", null, RecalculateNormalsAction)
|
||||
new ToolStripMenuItem("Smooth (Multiple Meshes)", null, MultiMeshSmoothNormals),
|
||||
new ToolStripMenuItem("Smooth", null, SmoothNormalsAction),
|
||||
new ToolStripMenuItem("Recalculate", null, RecalculateNormalsAction)
|
||||
));
|
||||
|
||||
ContextMenuStrip.Items.Add(new ToolStripMenuItem("UVs", null,
|
||||
@ -181,6 +182,17 @@ namespace Bfres.Structs
|
||||
UpdateVertexData();
|
||||
}
|
||||
|
||||
public FSHP GetShape(string Name)
|
||||
{
|
||||
for (int fshp = 0; fshp < shapes.Count; fshp++)
|
||||
{
|
||||
if (shapes[fshp].Text == Name)
|
||||
return shapes[fshp];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void FlipUvsVertical()
|
||||
{
|
||||
foreach (var shape in shapes)
|
||||
@ -212,6 +224,26 @@ namespace Bfres.Structs
|
||||
|
||||
UpdateVertexData();
|
||||
}
|
||||
|
||||
private void MultiMeshSmoothNormals(object sender, EventArgs args)
|
||||
{
|
||||
SmoothNormalsMultiMeshForm form = new SmoothNormalsMultiMeshForm();
|
||||
form.LoadMeshes(GetModelList());
|
||||
if (form.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
var SelectedMeshes = form.GetSelectedMeshes();
|
||||
|
||||
Cursor.Current = Cursors.WaitCursor;
|
||||
STGenericObject.SmoothNormals(SelectedMeshes);
|
||||
Cursor.Current = Cursors.Default;
|
||||
|
||||
foreach (var shp in shapes)
|
||||
shp.SaveVertexBuffer(GetResFileU() != null);
|
||||
|
||||
UpdateVertexData();
|
||||
}
|
||||
}
|
||||
|
||||
private void BatchAttributeEdit()
|
||||
{
|
||||
AttributeEditor editor = new AttributeEditor();
|
||||
|
@ -181,6 +181,7 @@ namespace Bfres.Structs
|
||||
ContextMenuStrip.Items.Add(uvMenu);
|
||||
|
||||
ToolStripMenuItem normalsMenu = new ToolStripMenuItem("Normals");
|
||||
normalsMenu.DropDownItems.Add(new ToolStripMenuItem("Smooth (Multiple Meshes)", null, MultiMeshSmoothNormals));
|
||||
normalsMenu.DropDownItems.Add(new ToolStripMenuItem("Smooth", null, SmoothNormals));
|
||||
normalsMenu.DropDownItems.Add(new ToolStripMenuItem("Invert", null, InvertNormals));
|
||||
normalsMenu.DropDownItems.Add(new ToolStripMenuItem("Recalculate", null, RecalculateNormals));
|
||||
@ -287,6 +288,23 @@ namespace Bfres.Structs
|
||||
UpdateVertexData();
|
||||
}
|
||||
|
||||
private void MultiMeshSmoothNormals(object sender, EventArgs args)
|
||||
{
|
||||
SmoothNormalsMultiMeshForm form = new SmoothNormalsMultiMeshForm();
|
||||
form.LoadMeshes(GetParentModel().GetModelList());
|
||||
if (form.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
var SelectedMeshes = form.GetSelectedMeshes();
|
||||
|
||||
Cursor.Current = Cursors.WaitCursor;
|
||||
SmoothNormals(SelectedMeshes);
|
||||
Cursor.Current = Cursors.Default;
|
||||
|
||||
SaveVertexBuffer(GetResFileU() != null);
|
||||
UpdateVertexData();
|
||||
}
|
||||
}
|
||||
|
||||
private void UVUnwrapPosition(object sender, EventArgs args)
|
||||
{
|
||||
Cursor.Current = Cursors.WaitCursor;
|
||||
|
103
Switch_FileFormatsMain/GUI/BFRES/SmoothNormalsMultiMeshForm.Designer.cs
generated
Normal file
103
Switch_FileFormatsMain/GUI/BFRES/SmoothNormalsMultiMeshForm.Designer.cs
generated
Normal file
@ -0,0 +1,103 @@
|
||||
namespace FirstPlugin
|
||||
{
|
||||
partial class SmoothNormalsMultiMeshForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.treeViewCustom1 = new Switch_Toolbox.Library.TreeViewCustom();
|
||||
this.stButton1 = new Switch_Toolbox.Library.Forms.STButton();
|
||||
this.stButton2 = new Switch_Toolbox.Library.Forms.STButton();
|
||||
this.contentContainer.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// contentContainer
|
||||
//
|
||||
this.contentContainer.Controls.Add(this.stButton2);
|
||||
this.contentContainer.Controls.Add(this.stButton1);
|
||||
this.contentContainer.Controls.Add(this.treeViewCustom1);
|
||||
this.contentContainer.Controls.SetChildIndex(this.treeViewCustom1, 0);
|
||||
this.contentContainer.Controls.SetChildIndex(this.stButton1, 0);
|
||||
this.contentContainer.Controls.SetChildIndex(this.stButton2, 0);
|
||||
//
|
||||
// treeViewCustom1
|
||||
//
|
||||
this.treeViewCustom1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.treeViewCustom1.BorderStyle = System.Windows.Forms.BorderStyle.None;
|
||||
this.treeViewCustom1.CheckBoxes = true;
|
||||
this.treeViewCustom1.ImageIndex = 0;
|
||||
this.treeViewCustom1.Location = new System.Drawing.Point(3, 27);
|
||||
this.treeViewCustom1.Name = "treeViewCustom1";
|
||||
this.treeViewCustom1.SelectedImageIndex = 0;
|
||||
this.treeViewCustom1.Size = new System.Drawing.Size(531, 328);
|
||||
this.treeViewCustom1.TabIndex = 11;
|
||||
this.treeViewCustom1.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.treeViewCustom1_AfterCheck);
|
||||
//
|
||||
// stButton1
|
||||
//
|
||||
this.stButton1.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.stButton1.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.stButton1.Location = new System.Drawing.Point(459, 361);
|
||||
this.stButton1.Name = "stButton1";
|
||||
this.stButton1.Size = new System.Drawing.Size(75, 23);
|
||||
this.stButton1.TabIndex = 12;
|
||||
this.stButton1.Text = "Cancel";
|
||||
this.stButton1.UseVisualStyleBackColor = false;
|
||||
//
|
||||
// stButton2
|
||||
//
|
||||
this.stButton2.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.stButton2.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.stButton2.Location = new System.Drawing.Point(378, 361);
|
||||
this.stButton2.Name = "stButton2";
|
||||
this.stButton2.Size = new System.Drawing.Size(75, 23);
|
||||
this.stButton2.TabIndex = 13;
|
||||
this.stButton2.Text = "Ok";
|
||||
this.stButton2.UseVisualStyleBackColor = false;
|
||||
this.stButton2.Click += new System.EventHandler(this.stButton2_Click);
|
||||
//
|
||||
// SmoothNormalsMultiMeshForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(549, 398);
|
||||
this.Name = "SmoothNormalsMultiMeshForm";
|
||||
this.Text = "Smooth Normals : Select Meshes";
|
||||
this.contentContainer.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private Switch_Toolbox.Library.TreeViewCustom treeViewCustom1;
|
||||
private Switch_Toolbox.Library.Forms.STButton stButton2;
|
||||
private Switch_Toolbox.Library.Forms.STButton stButton1;
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Switch_Toolbox.Library.Forms;
|
||||
using Switch_Toolbox.Library;
|
||||
using Bfres.Structs;
|
||||
|
||||
namespace FirstPlugin
|
||||
{
|
||||
public partial class SmoothNormalsMultiMeshForm : STForm
|
||||
{
|
||||
public SmoothNormalsMultiMeshForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public void LoadMeshes(List<FMDL> Models)
|
||||
{
|
||||
treeViewCustom1.BeginUpdate();
|
||||
for (int fmdl = 0; fmdl < Models.Count; fmdl++)
|
||||
{
|
||||
treeViewCustom1.Nodes.Add(new TreeNode(Models[fmdl].Text)
|
||||
{
|
||||
Tag = Models[fmdl],
|
||||
ImageKey = "model",
|
||||
SelectedImageKey = "model",
|
||||
});
|
||||
|
||||
for (int fshp = 0; fshp < Models[fmdl].shapes.Count; fshp++)
|
||||
{
|
||||
treeViewCustom1.Nodes[fmdl].Nodes.Add(new TreeNode(Models[fmdl].shapes[fshp].Text)
|
||||
{
|
||||
ImageKey = "mesh",
|
||||
SelectedImageKey = "mesh",
|
||||
});
|
||||
}
|
||||
}
|
||||
treeViewCustom1.EndUpdate();
|
||||
}
|
||||
|
||||
public List<STGenericObject> GetSelectedMeshes()
|
||||
{
|
||||
List<STGenericObject> Meshes = new List<STGenericObject>();
|
||||
foreach (TreeNode model in treeViewCustom1.Nodes)
|
||||
{
|
||||
foreach (TreeNode shape in model.Nodes)
|
||||
{
|
||||
if (shape.Checked)
|
||||
Meshes.Add(((FMDL)model.Tag).GetShape(shape.Text));
|
||||
}
|
||||
}
|
||||
return Meshes;
|
||||
}
|
||||
|
||||
private void stButton2_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (GetSelectedMeshes().Count == 0)
|
||||
{
|
||||
DialogResult = DialogResult.None;
|
||||
MessageBox.Show("Make sure there is atleast one mesh that is checked!");
|
||||
}
|
||||
}
|
||||
|
||||
private void treeViewCustom1_AfterCheck(object sender, TreeViewEventArgs e)
|
||||
{
|
||||
bool HasChildren = e.Node.Nodes.Count > 0;
|
||||
if (HasChildren)
|
||||
{
|
||||
foreach (TreeNode child in e.Node.Nodes)
|
||||
child.Checked = e.Node.Checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
120
Switch_FileFormatsMain/GUI/BFRES/SmoothNormalsMultiMeshForm.resx
Normal file
120
Switch_FileFormatsMain/GUI/BFRES/SmoothNormalsMultiMeshForm.resx
Normal file
@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
@ -279,6 +279,12 @@
|
||||
<Compile Include="GUI\BFRES\ParamAnim\ParamPatternMaterialEditor.Designer.cs">
|
||||
<DependentUpon>ParamPatternMaterialEditor.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="GUI\BFRES\SmoothNormalsMultiMeshForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="GUI\BFRES\SmoothNormalsMultiMeshForm.Designer.cs">
|
||||
<DependentUpon>SmoothNormalsMultiMeshForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="YAML\YamlAamp.cs" />
|
||||
<Compile Include="GUI\BCRES\BfresEditor.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
@ -1030,6 +1036,9 @@
|
||||
<EmbeddedResource Include="GUI\BFRES\Shape\BfresShapeEditor.resx">
|
||||
<DependentUpon>BfresShapeEditor.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="GUI\BFRES\SmoothNormalsMultiMeshForm.resx">
|
||||
<DependentUpon>SmoothNormalsMultiMeshForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="GUI\BFRES\SubFileEditor.resx">
|
||||
<DependentUpon>SubFileEditor.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1 +1 @@
|
||||
e402c1ea3b5c6e74ab734c29926ae6969018fbfc
|
||||
3779dc3a197c1f391b60c356a4d2114a1d71e61a
|
||||
|
@ -332,3 +332,4 @@ C:\Users\Nathan\Documents\GitHub\SwitchToolboxV1\Switch_FileFormatsMain\obj\Rele
|
||||
C:\Users\Nathan\Documents\GitHub\SwitchToolboxV1\Switch_FileFormatsMain\obj\Release\FirstPlugin.Forms.OdysseyCostumeSelector.resources
|
||||
C:\Users\Nathan\Documents\GitHub\SwitchToolboxV1\Switch_FileFormatsMain\obj\Release\FirstPlugin.Forms.MaterialPresetDialog.resources
|
||||
C:\Users\Nathan\Documents\GitHub\SwitchToolboxV1\Switch_FileFormatsMain\obj\Release\FirstPlugin.Forms.ParamPatternMaterialEditor.resources
|
||||
C:\Users\Nathan\Documents\GitHub\SwitchToolboxV1\Switch_FileFormatsMain\obj\Release\FirstPlugin.SmoothNormalsMultiMeshForm.resources
|
||||
|
Binary file not shown.
Binary file not shown.
@ -327,6 +327,98 @@ namespace Switch_Toolbox.Library
|
||||
}
|
||||
}
|
||||
|
||||
public static void SmoothNormals(List<STGenericObject> Shapes, int DisplayLODIndex = 0)
|
||||
{
|
||||
if (Shapes.Count == 0)
|
||||
return;
|
||||
|
||||
//List of duplicate vertices from multiple shapes
|
||||
//We will normalize each vertex with the same normal value to prevent seams
|
||||
List<Vertex> DuplicateVerts = new List<Vertex>();
|
||||
List<Vector3> NotDuplicateVerts = new List<Vector3>();
|
||||
|
||||
for (int s = 0; s < Shapes.Count; s++)
|
||||
{
|
||||
if (Shapes[s].vertices.Count < 3)
|
||||
continue;
|
||||
|
||||
Vector3[] normals = new Vector3[Shapes[s].vertices.Count];
|
||||
|
||||
List<int> f = Shapes[s].lodMeshes[DisplayLODIndex].getDisplayFace();
|
||||
|
||||
for (int v = 0; v < Shapes[s].lodMeshes[DisplayLODIndex].displayFaceSize; v += 3)
|
||||
{
|
||||
Vertex v1 = Shapes[s].vertices[f[v]];
|
||||
Vertex v2 = Shapes[s].vertices[f[v + 1]];
|
||||
Vertex v3 = Shapes[s].vertices[f[v + 2]];
|
||||
Vector3 nrm = Shapes[s].CalculateNormal(v1, v2, v3);
|
||||
|
||||
if (NotDuplicateVerts.Contains(v1.pos)) DuplicateVerts.Add(v1); else NotDuplicateVerts.Add(v1.pos);
|
||||
if (NotDuplicateVerts.Contains(v2.pos)) DuplicateVerts.Add(v2); else NotDuplicateVerts.Add(v2.pos);
|
||||
if (NotDuplicateVerts.Contains(v3.pos)) DuplicateVerts.Add(v3); else NotDuplicateVerts.Add(v3.pos);
|
||||
|
||||
normals[f[v + 0]] += nrm;
|
||||
normals[f[v + 1]] += nrm;
|
||||
normals[f[v + 2]] += nrm;
|
||||
}
|
||||
|
||||
for (int n = 0; n < normals.Length; n++)
|
||||
{
|
||||
Shapes[s].vertices[n].nrm = normals[n].Normalized();
|
||||
}
|
||||
}
|
||||
|
||||
//Smooth normals normally
|
||||
for (int s = 0; s < Shapes.Count; s++)
|
||||
{
|
||||
// Compare each vertex with all the remaining vertices. This might skip some.
|
||||
for (int i = 0; i < Shapes[s].vertices.Count; i++)
|
||||
{
|
||||
Vertex v = Shapes[s].vertices[i];
|
||||
|
||||
for (int j = i + 1; j < Shapes[s].vertices.Count; j++)
|
||||
{
|
||||
Vertex v2 = Shapes[s].vertices[j];
|
||||
|
||||
if (v == v2)
|
||||
continue;
|
||||
float dis = (float)Math.Sqrt(Math.Pow(v.pos.X - v2.pos.X, 2) + Math.Pow(v.pos.Y - v2.pos.Y, 2) + Math.Pow(v.pos.Z - v2.pos.Z, 2));
|
||||
if (dis <= 0f) // Extra smooth
|
||||
{
|
||||
Vector3 nn = ((v2.nrm + v.nrm) / 2).Normalized();
|
||||
v.nrm = nn;
|
||||
v2.nrm = nn;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Now do the same but for fixing duplicate vertices
|
||||
for (int s = 0; s < Shapes.Count; s++)
|
||||
{
|
||||
//Smooth duplicate normals
|
||||
for (int i = 0; i < Shapes[s].vertices.Count; i++)
|
||||
{
|
||||
Vertex v = Shapes[s].vertices[i];
|
||||
|
||||
for (int j = i + 1; j < DuplicateVerts.Count; j++)
|
||||
{
|
||||
Vertex v2 = DuplicateVerts[j];
|
||||
if (v.pos == v2.pos)
|
||||
{
|
||||
float dis = (float)Math.Sqrt(Math.Pow(v.pos.X - v2.pos.X, 2) + Math.Pow(v.pos.Y - v2.pos.Y, 2) + Math.Pow(v.pos.Z - v2.pos.Z, 2));
|
||||
if (dis <= 0f) // Extra smooth
|
||||
{
|
||||
Vector3 nn = ((v2.nrm + v.nrm) / 2).Normalized();
|
||||
v.nrm = nn;
|
||||
v2.nrm = nn;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SmoothNormals()
|
||||
{
|
||||
if (vertices.Count < 3)
|
||||
|
Loading…
Reference in New Issue
Block a user