From 540a3ffe4a9be39c346973e1f958c02398ce3f8a Mon Sep 17 00:00:00 2001
From: Ian C <ianc@noddybox.co.uk>
Date: Sun, 24 Apr 2005 01:44:23 +0000
Subject: Initial working version

---
 groupexplode/config.cpp  |   7 ++
 groupexplode/config.h    |   4 +
 groupexplode/dialog.cpp  |   5 ++
 groupexplode/dialog.h    |   1 +
 groupexplode/main.cpp    | 230 ++++++++++++++++++++++++++++++++++++++++++++++-
 groupexplode/resource.h  |   1 +
 groupexplode/resource.rc |   9 +-
 7 files changed, 249 insertions(+), 8 deletions(-)

diff --git a/groupexplode/config.cpp b/groupexplode/config.cpp
index 59f87f0..740fd9d 100644
--- a/groupexplode/config.cpp
+++ b/groupexplode/config.cpp
@@ -27,6 +27,7 @@
 
 bool		Config::m_init=false;
 bool		Config::m_rmOrig=false;
+bool		Config::m_collate=false;
 
 void Config::Load()
 {
@@ -35,12 +36,14 @@ void Config::Load()
     	m_init=true;
 
 	m_rmOrig=false;
+	m_collate=true;
 
 #if REGISTRY_USAGE!=0
 
 	W32DLib::Registry r(HKEY_CURRENT_USER,KEY);
 
 	r.Read("RemoveOriginal",m_rmOrig);
+	r.Read("Collate",m_collate);
 
 #endif
 
@@ -54,9 +57,13 @@ void Config::Save()
     W32DLib::Registry r(HKEY_CURRENT_USER,KEY,REGISTRY_USAGE==2);
 
     r.Write("RemoveOriginal",m_rmOrig);
+    r.Write("Collate",m_collate);
 
 #endif
 }
 
 bool Config::RemoveOriginal()			{return m_rmOrig;}
 void Config::RemoveOriginal(bool flag)		{m_rmOrig=flag;}
+
+bool Config::Collate()				{return m_collate;}
+void Config::Collate(bool flag)			{m_collate=flag;}
diff --git a/groupexplode/config.h b/groupexplode/config.h
index 8d00356..302e2fb 100644
--- a/groupexplode/config.h
+++ b/groupexplode/config.h
@@ -36,12 +36,16 @@ public:
 	static bool		RemoveOriginal();
 	static void		RemoveOriginal(bool flag);
 
+	static bool		Collate();
+	static void		Collate(bool flag);
+
 private:
 	Config();
 
 	static bool		m_init;
 
 	static bool		m_rmOrig;
+	static bool		m_collate;
 };
 
 #endif // CONFIG_H
diff --git a/groupexplode/dialog.cpp b/groupexplode/dialog.cpp
index 6816de9..c8d30f7 100644
--- a/groupexplode/dialog.cpp
+++ b/groupexplode/dialog.cpp
@@ -33,6 +33,7 @@ Dialog::Dialog(const StringList& groups)
 			    : m_ok(this,IDC_OKBUT,0)
 			    , m_cancel(this,IDC_CANCELBUT,0)
 			    , m_rmOrig(this,IDC_RMORIG,0)
+			    , m_collate(this,IDC_COLLATE,0)
 			    , m_groups(this,IDC_GROUP,0)
 			    , m_sel(-1)
 			    , m_groupList(groups)
@@ -67,6 +68,7 @@ void Dialog::OnInit()
 
     m_groups.SelectedIndex(0);
 
+    m_collate.SetState(Config::Collate());
     m_rmOrig.SetState(Config::RemoveOriginal());
 }
 
@@ -82,7 +84,10 @@ int Dialog::GetSelectedGroup()
 
 BOOL Dialog::OnOK(UINT msg, WPARAM wp, LPARAM lp) 
 {
+    Config::Collate(m_collate.GetState());
     Config::RemoveOriginal(m_rmOrig.GetState());
+    Config::Save();
+
     m_sel=m_groups.SelectedIndex();
 
     Close(IDOK);
diff --git a/groupexplode/dialog.h b/groupexplode/dialog.h
index f68efa5..4620eec 100644
--- a/groupexplode/dialog.h
+++ b/groupexplode/dialog.h
@@ -43,6 +43,7 @@ private:
     W32DLib::Button	m_ok;
     W32DLib::Button	m_cancel;
     W32DLib::AutoCheck	m_rmOrig;
+    W32DLib::AutoCheck	m_collate;
     W32DLib::ComboBox	m_groups;
     int			m_sel;
     const StringList&	m_groupList;
diff --git a/groupexplode/main.cpp b/groupexplode/main.cpp
index dbee433..2979b7b 100644
--- a/groupexplode/main.cpp
+++ b/groupexplode/main.cpp
@@ -20,10 +20,229 @@
 //
 static const char rcs_id[]="$Id$";
 
+#include <algorithm>
+#include <cstdio>
+#include <cstring>
+#include <stdarg.h>
+
 #include "mingwms.h"
 #include "msLib.h"
 
 #include "dialog.h"
+#include "config.h"
+
+
+// ----------------------------------------------------------------------
+// TYPES
+//
+typedef std::vector<int>	IntList;
+typedef std::vector<IntList>	TriangleList;
+
+class IndexMap : public std::map<int,int>
+{
+public:
+	IndexMap() : std::map<int,int> () {}
+
+	bool Has(int key)
+	{
+	    return find(key)!=end();
+	}
+};
+
+
+// ----------------------------------------------------------------------
+// UTILS
+//
+static bool VecMatch(const msVec3 v1, const msVec3 v2)
+{
+    for(int f=0;f<3;f++)
+    {
+    	if (v1[f]!=v2[f])
+	{
+	    return false;
+	}
+    }
+
+    return true;
+}
+
+
+static void CopyVec3(msVec3 to, const msVec3 from)
+{
+    for(int f=0;f<3;f++)
+    {
+    	to[f]=from[f];
+    }
+}
+
+
+static bool VertexNormalsMatch(msMesh *m, int t1, int t2)
+{
+    msVec3 v1;
+    msVec3 v2;
+
+    for(int f=0;f<3;f++)
+    {
+	msMesh_GetVertexNormalAt(m,(m->pTriangles+t1)->nNormalIndices[f],v1);
+	msMesh_GetVertexNormalAt(m,(m->pTriangles+t2)->nNormalIndices[f],v2);
+
+	if (!VecMatch(v1,v2))
+	{
+	    return false;
+	}
+    }
+
+    return true;
+}
+
+
+static void SetName(char *name, const char *base, int num)
+{
+    int len=std::min(static_cast<int>(std::strlen(base)),MS_MAX_NAME-10);
+
+    std::sprintf(name,"%*.*s_%d",len,len,base,num);
+}
+
+
+static void DEBUG(const char *fmt, ...)
+{
+    static char buff[4096];
+
+    va_list va;
+    va_start(va,fmt);
+    std::vsprintf(buff,fmt,va);
+    va_end(va);
+
+    W32DLib::Common::Message(0,"DEBUG",buff);
+}
+
+
+// ----------------------------------------------------------------------
+// GROUP SPLITTER -- This is *very* ugly code, so look away if you're of
+//		     a nervous disposition.
+//
+static void SplitGroup(msModel *model, int group)
+{
+    TriangleList list;
+    msMesh *orig_mesh=msModel_GetMeshAt(model,group);
+    int num=orig_mesh->nNumTriangles;
+    bool *used=new bool[num];
+    std::string name=orig_mesh->szName;
+
+    for(int f=0;f<num;f++)
+    {
+    	used[f]=false;
+    }
+
+    for(int f=0;f<num;f++)
+    {
+    	if (!used[f])
+	{
+	    IntList sublist;
+
+	    sublist.push_back(f);
+	    used[f]=true;
+
+	    if (Config::Collate())
+	    {
+	    	for(int i=0;i<num;i++)
+		{
+		    if (!used[i] && VertexNormalsMatch(orig_mesh,f,i))
+		    {
+		    	sublist.push_back(i);
+			used[i]=true;
+		    }
+		}
+	    }
+
+	    list.push_back(sublist);
+	}
+    }
+
+    delete[] used;
+
+    TriangleList::const_iterator i;
+    IntList::const_iterator j;
+
+    num=1;
+
+    for(i=list.begin();i!=list.end();++i)
+    {
+	IndexMap vmap;
+	IndexMap nmap;
+
+	msMesh *mesh=msModel_GetMeshAt(model,msModel_AddMesh(model));
+
+	SetName(mesh->szName,name.c_str(),num++);
+
+	mesh->nMaterialIndex=orig_mesh->nMaterialIndex;
+
+    	for(j=i->begin();j!=i->end();++j)
+	{
+	    msTriangle *old_tri=msMesh_GetTriangleAt(orig_mesh,*j);
+	    msTriangle *new_tri=msMesh_GetTriangleAt
+	    				(mesh,msMesh_AddTriangle(mesh));
+
+	    CopyVec3(new_tri->Normal,old_tri->Normal);
+	    new_tri->nFlags=old_tri->nFlags;
+
+	    for(int f=0;f<3;f++)
+	    {
+		int old_vi=old_tri->nVertexIndices[f];
+		int new_vi;
+		int old_ni=old_tri->nNormalIndices[f];
+		int new_ni;
+
+		if (vmap.Has(old_vi))
+		{
+		    new_vi=vmap[old_vi];
+		}
+		else
+		{
+		    new_vi=msMesh_AddVertex(mesh);
+		    vmap[old_vi]=new_vi;
+		}
+
+		if (nmap.Has(old_ni))
+		{
+		    new_ni=nmap[old_ni];
+		}
+		else
+		{
+		    new_ni=msMesh_AddVertexNormal(mesh);
+		    nmap[old_ni]=new_ni;
+		}
+
+	    	msVertex *old_ver=msMesh_GetVertexAt(orig_mesh,old_vi);
+	    	msVertex *new_ver=msMesh_GetVertexAt(mesh,new_vi);
+
+		*new_ver=*old_ver;
+
+		msVec3 normal;
+
+		msMesh_GetVertexNormalAt(orig_mesh,old_ni,normal);
+		msMesh_SetVertexNormalAt(mesh,new_ni,normal);
+
+		new_tri->nVertexIndices[f]=new_vi;
+		new_tri->nNormalIndices[f]=new_ni;
+	    }
+	}
+    }
+
+    if (Config::RemoveOriginal())
+    {
+    	msMesh_Destroy(orig_mesh);
+
+	for(int f=group;f<model->nNumMeshes-1;f++)
+	{
+	    model->pMeshes[f]=model->pMeshes[f+1];
+	}
+
+	model->nNumMeshes--;
+	model->nNumAllocedMeshes--;
+    }
+}
+
 
 // ----------------------------------------------------------------------
 // MINGW WRAPPER FUNCTIONS
@@ -38,8 +257,6 @@ static int Execute(msModel* model)
     W32DLib::Common::Initialise();
     W32DLib::Common::MessageTitle("Group Exploder");
 
-    // Unfortunately this is the best you can hope for with Milkshape...
-    //
     HWND parent=GetForegroundWindow();
 
     if (!model)
@@ -67,12 +284,17 @@ static int Execute(msModel* model)
 
     if (dlg.ShowModal(W32DLib::Common::GetInstance(),parent)==IDOK)
     {
-    	int sel=dlg.GetSelectedGroup();
+        SetCursor(LoadCursor(NULL,IDC_WAIT));
+	SplitGroup(model,dlg.GetSelectedGroup());
+	SetCursor(LoadCursor(NULL,IDC_ARROW));
+
+	return MS_MODEL_CHANGED;
     }
 
-    return 1;
+    return MS_MODEL_UNCHANGED;
 }
 
+
 PLUGIN_SPEC void* CreatePlugIn()
 {
     return CreateMINGWPlugin(MS_TYPE_TOOL, GetTitle, Execute);
diff --git a/groupexplode/resource.h b/groupexplode/resource.h
index 1da17e5..445191d 100644
--- a/groupexplode/resource.h
+++ b/groupexplode/resource.h
@@ -28,3 +28,4 @@
 #define IDC_CANCELBUT	4
 #define IDC_GROUP	5
 #define IDC_RMORIG	6
+#define IDC_COLLATE	7
diff --git a/groupexplode/resource.rc b/groupexplode/resource.rc
index 31ddf19..470ba45 100644
--- a/groupexplode/resource.rc
+++ b/groupexplode/resource.rc
@@ -1,7 +1,7 @@
 #include <windows.h>
 #include "resource.h"
 
-GR_DIALOG DIALOG  100, 100, 200, 85
+GR_DIALOG DIALOG  100, 100, 200, 105
 		STYLE WS_POPUP | WS_BORDER
 		CAPTION "Group Exploder"
 		FONT 8,"MS Shell Dlg"
@@ -10,8 +10,9 @@ GR_DIALOG DIALOG  100, 100, 200, 85
 
     COMBOBOX IDC_GROUP, 10, 20, 180, 200, CBS_DROPDOWNLIST | WS_TABSTOP 
 
-    AUTOCHECKBOX "Remove Original Group", IDC_RMORIG, 10,40,180,18
+    AUTOCHECKBOX "Remove original mesh", IDC_RMORIG, 10,40,180,18
+    AUTOCHECKBOX "Collate triangles with identical normals", IDC_COLLATE, 10,60,180,18
 
-    PUSHBUTTON "OK", IDC_OKBUT, 10,60,80,18
-    PUSHBUTTON "Cancel", IDC_CANCELBUT, 110,60,80,18
+    PUSHBUTTON "OK", IDC_OKBUT, 10,80,80,18
+    PUSHBUTTON "Cancel", IDC_CANCELBUT, 110,80,80,18
 }
-- 
cgit v1.2.3