diff --git a/CHANGES b/CHANGES
index 560d252d..5447e93c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,15 @@
This is the changelog for developers, != changelog for the end user
that we distribute with the binaries. (see changelog)
+11/09/2006
+namespace
+- Added sunplug (Mapcoordinator-plugin for ET) by Topsun
+ ET only: setting mapcoordsmins and mapcoordsmaxs in the worldspawn
+ Tester: Shaderman
+- Added brushexport Plugin by namespace
+ Exports selected brushes as wavefront object.
+ Tester: Shaderman
+
11/09/2006
namespace
- Fixed compile error on x86_64, see http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=1105
diff --git a/GtkRadiant.sln b/GtkRadiant.sln
index 2eacd630..544534f3 100644
--- a/GtkRadiant.sln
+++ b/GtkRadiant.sln
@@ -150,6 +150,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bobtoolz", "contrib\bobtool
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sunplug", "contrib\sunplug\sunplug.vcproj", "{46B36F0C-5E17-458E-AE6F-AECE52F66EDF}"
+ ProjectSection(ProjectDependencies) = postProject
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "brushexport", "contrib\brushexport\brushexport.vcproj", "{46B36F0C-5E17-458E-AE6F-AECE52F66EDE}"
+ ProjectSection(ProjectDependencies) = postProject
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfiguration) = preSolution
Debug = Debug
@@ -280,6 +288,14 @@ Global
{B20364D1-4329-4D4E-B9CE-C9767618FDD6}.Debug.Build.0 = Debug|Win32
{B20364D1-4329-4D4E-B9CE-C9767618FDD6}.Release.ActiveCfg = Release|Win32
{B20364D1-4329-4D4E-B9CE-C9767618FDD6}.Release.Build.0 = Release|Win32
+ {46B36F0C-5E17-458E-AE6F-AECE52F66EDF}.Debug.ActiveCfg = Debug|Win32
+ {46B36F0C-5E17-458E-AE6F-AECE52F66EDF}.Debug.Build.0 = Debug|Win32
+ {46B36F0C-5E17-458E-AE6F-AECE52F66EDF}.Release.ActiveCfg = Release|Win32
+ {46B36F0C-5E17-458E-AE6F-AECE52F66EDF}.Release.Build.0 = Release|Win32
+ {46B36F0C-5E17-458E-AE6F-AECE52F66EDE}.Debug.ActiveCfg = Debug|Win32
+ {46B36F0C-5E17-458E-AE6F-AECE52F66EDE}.Debug.Build.0 = Debug|Win32
+ {46B36F0C-5E17-458E-AE6F-AECE52F66EDE}.Release.ActiveCfg = Release|Win32
+ {46B36F0C-5E17-458E-AE6F-AECE52F66EDE}.Release.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EndGlobalSection
diff --git a/SConscript b/SConscript
index ed437ba3..f33c75fe 100644
--- a/SConscript
+++ b/SConscript
@@ -473,6 +473,21 @@ prtview_lib = prtview_env.SharedLibrarySafe(target='prtview', source=prtview_lst
prtview_env.Depends(prtview_lib, profile_lib)
prtview_env.Install(INSTALL + '/plugins', prtview_lib)
+
+brushexport_env = module_env.Copy()
+brushexport_lst = build_list('contrib/brushexport', 'plugin.cpp')
+brushexport_env.useGlib2()
+brushexport_env.useGtk2()
+brushexport_lib = brushexport_env.SharedLibrarySafe(target='brushexport', source=brushexport_lst, LIBPATH='libs')
+brushexport_env.Install(INSTALL + '/plugins', brushexport_lib)
+
+sunplug_env = module_env.Copy()
+sunplug_lst = build_list('contrib/sunplug', 'sunplug.cpp')
+sunplug_env.useGlib2()
+sunplug_env.useGtk2()
+sunplug_lib = sunplug_env.SharedLibrarySafe(target='sunplug', source=sunplug_lst, LIBPATH='libs')
+sunplug_env.Install(INSTALL + '/plugins', sunplug_lib)
+
#gensurf_lst = build_list('contrib/gtkgensurf',
#'bitmap.cpp dec.cpp face.cpp font.cpp gendlgs.cpp genmap.cpp gensurf.cpp heretic.cpp plugin.cpp view.cpp triangle.c')
#bob_env.SharedLibrarySafe(target='gensurf', source=gensurf_lst)
diff --git a/contrib/brushexport/brushexport.def b/contrib/brushexport/brushexport.def
new file mode 100644
index 00000000..44d97f3f
--- /dev/null
+++ b/contrib/brushexport/brushexport.def
@@ -0,0 +1,7 @@
+; brushexport.def : Declares the module parameters for the DLL.
+
+LIBRARY "BRUSHEXPORT"
+
+EXPORTS
+ ; Explicit exports can go here
+ Radiant_RegisterModules @1
diff --git a/contrib/brushexport/brushexport.vcproj b/contrib/brushexport/brushexport.vcproj
new file mode 100644
index 00000000..a978947a
--- /dev/null
+++ b/contrib/brushexport/brushexport.vcproj
@@ -0,0 +1,163 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/contrib/brushexport/plugin.cpp b/contrib/brushexport/plugin.cpp
new file mode 100644
index 00000000..8954473d
--- /dev/null
+++ b/contrib/brushexport/plugin.cpp
@@ -0,0 +1,236 @@
+/*
+Copyright (C) 2006, Thomas Nitschke.
+All Rights Reserved.
+
+This file is part of GtkRadiant.
+
+GtkRadiant is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+GtkRadiant is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GtkRadiant; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#include "plugin.h"
+
+#include "iplugin.h"
+#include "qerplugin.h"
+
+#include
+
+#include "debugging/debugging.h"
+#include "string/string.h"
+#include "modulesystem/singletonmodule.h"
+#include "stream/textfilestream.h"
+#include "stream/stringstream.h"
+#include "gtkutil/messagebox.h"
+#include "gtkutil/filechooser.h"
+
+#include "ibrush.h"
+#include "iscenegraph.h"
+#include "iselection.h"
+#include "ifilesystem.h"
+#include "ifiletypes.h"
+
+#include "../../radiant/brush.h"
+
+namespace BrushExport
+{
+ GtkWindow* g_mainwnd;
+
+ class CExportFormatWavefront : public BrushVisitor
+ {
+ TextFileOutputStream& m_file;
+
+ StringOutputStream vertexbuffer;
+ StringOutputStream texcoordbuffer;
+ StringOutputStream facebuffer;
+
+ size_t vertices;
+ size_t exported;
+
+ public:
+
+ CExportFormatWavefront(TextFileOutputStream& file)
+ : m_file(file)
+ {
+ exported = 0;
+ vertices = 0;
+ }
+
+ virtual ~CExportFormatWavefront(void) {}
+
+ void visit(scene::Instance& instance)
+ {
+ BrushInstance* bptr = InstanceTypeCast::cast(instance);
+ if(bptr)
+ {
+ Brush& brush(bptr->getBrush());
+
+ m_file << "\ng " << brush.name() << exported << "\n";
+
+ brush.forEachFace(*this);
+
+ m_file << vertexbuffer.c_str() << "\n";
+ m_file << texcoordbuffer.c_str();
+ m_file << facebuffer.c_str() << "\n";
+
+ vertexbuffer.clear();
+ texcoordbuffer.clear();
+ facebuffer.clear();
+ ++exported;
+ }
+ }
+
+ void visit(Face& face) const
+ {
+ // cast the stupid const away
+ const_cast(this)->visit(face);
+ }
+
+ void visit(Face& face)
+ {
+ size_t v_start = vertices;
+ const Winding& w(face.getWinding());
+ for(size_t i = 0; i < w.numpoints; ++i)
+ {
+ vertexbuffer << "v " << w[i].vertex.x() << " " << w[i].vertex.y() << " " << w[i].vertex.z() << "\n";
+ texcoordbuffer << "vt " << w[i].texcoord.x() << " " << w[i].texcoord.y() << "\n";
+ ++vertices;
+ }
+
+ facebuffer << "\nf";
+ for(size_t i = v_start; i < vertices; ++i)
+ facebuffer << " " << i+1 << "/" << i+1;
+ }
+ };
+
+ /**
+ Exporterclass which will pass every visit-call
+ to a special formatexporter.
+ */
+ template
+ class CExporter : public SelectionSystem::Visitor
+ {
+ public:
+ CExporter(TextFileOutputStream& file)
+ : m_exporter(file)
+ {}
+
+ virtual ~CExporter(void) {}
+
+ void visit(scene::Instance& instance) const
+ {
+ m_exporter.visit(instance);
+ }
+
+ private:
+ mutable TExporterFormat m_exporter;
+ };
+
+ template
+ void export_selected(TextFileOutputStream& file)
+ {
+ CExporter exporter(file);
+ GlobalSelectionSystem().foreachSelected(exporter);
+ }
+
+ const char* init(void* hApp, void* pMainWidget)
+ {
+ g_mainwnd = (GtkWindow*)pMainWidget;
+ ASSERT_NOTNULL(g_mainwnd);
+ return "";
+ }
+ const char* getName()
+ {
+ return "Brush export Plugin";
+ }
+ const char* getCommandList()
+ {
+ return "Export selected as Wavefront Object;About";
+ }
+ const char* getCommandTitleList()
+ {
+ return "";
+ }
+
+ void dispatch(const char* command, float* vMin, float* vMax, bool bSingleBrush)
+ {
+ if(string_equal(command, "About"))
+ {
+ GlobalRadiant().m_pfnMessageBox(GTK_WIDGET(g_mainwnd), "Brushexport plugin v 1.0 by namespace (www.codecreator.net)\n"
+ "Enjoy!\n\nSend feedback to spam@codecreator.net", "About me...",
+ eMB_OK,
+ eMB_ICONDEFAULT);
+ }
+ else if(string_equal(command, "Export selected as Wavefront Object"))
+ {
+ if(const char* path = GlobalRadiant().m_pfnFileDialog(GTK_WIDGET(g_mainwnd), false, "Save as Obj", 0, 0))
+ {
+ TextFileOutputStream file(path);
+ if(file.failed())
+ {
+ GlobalRadiant().m_pfnMessageBox(GTK_WIDGET(g_mainwnd), "Unable to write to file", "Error",
+ eMB_OK,
+ eMB_ICONERROR);
+ }
+ else
+ {
+ export_selected(file);
+ }
+ }
+ }
+ }
+
+} // namespace
+
+class BrushExportDependencies :
+ public GlobalRadiantModuleRef,
+ public GlobalFiletypesModuleRef,
+ public GlobalBrushModuleRef,
+ public GlobalFileSystemModuleRef,
+ public GlobalSceneGraphModuleRef,
+ public GlobalSelectionModuleRef
+{
+public:
+ BrushExportDependencies(void)
+ : GlobalBrushModuleRef(GlobalRadiant().getRequiredGameDescriptionKeyValue("brushtypes"))
+ {}
+};
+
+class BrushExportModule : public TypeSystemRef
+{
+ _QERPluginTable m_plugin;
+public:
+ typedef _QERPluginTable Type;
+ STRING_CONSTANT(Name, "brushexport");
+
+ BrushExportModule()
+ {
+ m_plugin.m_pfnQERPlug_Init = &BrushExport::init;
+ m_plugin.m_pfnQERPlug_GetName = &BrushExport::getName;
+ m_plugin.m_pfnQERPlug_GetCommandList = &BrushExport::getCommandList;
+ m_plugin.m_pfnQERPlug_GetCommandTitleList = &BrushExport::getCommandTitleList;
+ m_plugin.m_pfnQERPlug_Dispatch = &BrushExport::dispatch;
+ }
+ _QERPluginTable* getTable()
+ {
+ return &m_plugin;
+ }
+};
+
+typedef SingletonModule SingletonBrushExportModule;
+SingletonBrushExportModule g_BrushExportModule;
+
+extern "C" void RADIANT_DLLEXPORT Radiant_RegisterModules(ModuleServer& server)
+{
+ initialiseModule(server);
+ g_BrushExportModule.selfRegister();
+}
diff --git a/contrib/brushexport/plugin.h b/contrib/brushexport/plugin.h
new file mode 100644
index 00000000..99396819
--- /dev/null
+++ b/contrib/brushexport/plugin.h
@@ -0,0 +1,25 @@
+/*
+Copyright (C) 2006, Thomas Nitschke.
+All Rights Reserved.
+
+This file is part of GtkRadiant.
+
+GtkRadiant is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+GtkRadiant is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GtkRadiant; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#if !defined(INCLUDED_BRUSH_EXPORT_H)
+#define INCLUDED_BRUSH_EXPORT_H
+
+#endif
diff --git a/contrib/sunplug/sunplug.cpp b/contrib/sunplug/sunplug.cpp
new file mode 100644
index 00000000..67103018
--- /dev/null
+++ b/contrib/sunplug/sunplug.cpp
@@ -0,0 +1,462 @@
+/*
+Sunplug plugin for GtkRadiant
+Copyright (C) 2004 Topsun
+Thanks to SPoG for help!
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "sunplug.h"
+
+#include "debugging/debugging.h"
+
+#include "iplugin.h"
+
+#include "string/string.h"
+#include "modulesystem/singletonmodule.h"
+
+#include "iundo.h" // declaration of undo system
+#include "ientity.h" // declaration of entity system
+#include "iscenegraph.h" // declaration of datastructure of the map
+
+#include "scenelib.h" // declaration of datastructure of the map
+#include "qerplugin.h" // declaration to use other interfaces as a plugin
+
+#include // to display something with gtk (windows, buttons etc.), the whole package might not be necessary
+
+void about_plugin_window();
+void MapCoordinator();
+
+#ifdef __linux__
+// linux itoa implementation
+char* itoa( int value, char* result, int base )
+{
+ // check that the base if valid
+ if (base < 2 || base > 16)
+ {
+ *result = 0;
+ return result;
+ }
+
+ char* out = result;
+ int quotient = value;
+
+ do
+ {
+ *out = "0123456789abcdef"[abs(quotient % base)];
+ ++out;
+
+ quotient /= base;
+ } while (quotient);
+
+ // Only apply negative sign for base 10
+ if( value < 0 && base == 10)
+ *out++ = '-';
+
+ std::reverse(result, out);
+
+ *out = 0;
+ return result;
+}
+#endif
+
+typedef struct _mapcoord_setting_packet {
+ GtkSpinButton *spinner1, *spinner2, *spinner3, *spinner4;
+ Entity* worldspawn;
+} mapcoord_setting_packet;
+
+static int map_minX, map_maxX, map_minY, map_maxY;
+static int minX, maxX, minY, maxY;
+mapcoord_setting_packet msp;
+
+// **************************
+// ** find entities by class ** from radiant/map.cpp
+// **************************
+class EntityFindByClassname : public scene::Graph::Walker
+{
+ const char* m_name;
+ Entity*& m_entity;
+public:
+ EntityFindByClassname(const char* name, Entity*& entity) : m_name(name), m_entity(entity)
+ {
+ m_entity = 0;
+ }
+ bool pre(const scene::Path& path, scene::Instance& instance) const
+ {
+ if(m_entity == 0)
+ {
+ Entity* entity = Node_getEntity(path.top());
+ if(entity != 0
+ && string_equal(m_name, entity->getKeyValue("classname")))
+ {
+ m_entity = entity;
+ }
+ }
+ return true;
+ }
+};
+
+Entity* Scene_FindEntityByClass(const char* name)
+{
+ Entity* entity;
+ GlobalSceneGraph().traverse(EntityFindByClassname(name, entity));
+ return entity;
+}
+
+// **************************
+// ** GTK callback functions **
+// **************************
+
+static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ /* If you return FALSE in the "delete_event" signal handler,
+ * GTK will emit the "destroy" signal. Returning TRUE means
+ * you don't want the window to be destroyed.
+ * This is useful for popping up 'are you sure you want to quit?'
+ * type dialogs. */
+
+ return FALSE;
+}
+
+// destroy widget if destroy signal is passed to widget
+static void destroy(GtkWidget *widget, gpointer data)
+{
+ gtk_widget_destroy(widget);
+}
+
+// function for close button to destroy the toplevel widget
+static void close_window(GtkWidget *widget, gpointer data)
+{
+ gtk_widget_destroy(gtk_widget_get_toplevel(widget));
+}
+
+// callback function to assign the optimal mapcoords to the spinboxes
+static void input_optimal(GtkWidget *widget, gpointer data)
+{
+ gtk_spin_button_set_value(msp.spinner1, minX);
+ gtk_spin_button_set_value(msp.spinner2, maxY);
+ gtk_spin_button_set_value(msp.spinner3, maxX);
+ gtk_spin_button_set_value(msp.spinner4, minY);
+}
+
+// Spinner return value function
+gint grab_int_value(GtkSpinButton *a_spinner, gpointer user_data) {
+ return gtk_spin_button_get_value_as_int(a_spinner);
+}
+
+// write the values of the Spinner-Boxes to the worldspawn
+static void set_coordinates(GtkWidget *widget, gpointer data)
+{
+ //Str str_min, str_max;
+ char buffer[10], str_min[20], str_max[20];
+
+ itoa(gtk_spin_button_get_value_as_int(msp.spinner1), str_min, 10);
+ itoa(gtk_spin_button_get_value_as_int(msp.spinner2), buffer, 10);
+ strcat(str_min, " ");
+ strcat(str_min, buffer);
+ msp.worldspawn->setKeyValue("mapcoordsmins", str_min);
+
+ itoa(gtk_spin_button_get_value_as_int(msp.spinner3), str_max, 10);
+ itoa(gtk_spin_button_get_value_as_int(msp.spinner4), buffer, 10);
+ strcat(str_max, " ");
+ strcat(str_max, buffer);
+ UndoableCommand undo("SunPlug.entitySetMapcoords");
+ msp.worldspawn->setKeyValue("mapcoordsmaxs", str_max);
+
+ close_window(widget, NULL);
+}
+
+class SunPlugPluginDependencies :
+ public GlobalRadiantModuleRef, // basic class for all other module refs
+ public GlobalUndoModuleRef, // used to say radiant that something has changed and to undo that
+ public GlobalSceneGraphModuleRef, // necessary to handle data in the mapfile (change, retrieve data)
+ public GlobalEntityModuleRef // to access and modify the entities
+{
+public:
+ SunPlugPluginDependencies() :
+ GlobalEntityModuleRef(GlobalRadiant().getRequiredGameDescriptionKeyValue("entities"))//,
+ {
+ }
+};
+
+// *************************
+// ** standard plugin stuff **
+// *************************
+namespace SunPlug
+{
+ GtkWindow* main_window;
+ char MenuList[100] = "";
+
+ const char* init(void* hApp, void* pMainWidget)
+ {
+ main_window = GTK_WINDOW(pMainWidget);
+ return "Initializing SunPlug for GTKRadiant";
+ }
+ const char* getName()
+ {
+ return "SunPlug"; // name that is shown in the menue
+ }
+ const char* getCommandList()
+ {
+ const char about[] = "About...";
+ const char etMapCoordinator[] = ";ET-MapCoordinator";
+
+ strcat(MenuList, about);
+ if (strncmp(GlobalRadiant().getGameName(), "etmain", 6) == 0) strcat(MenuList, etMapCoordinator);
+ return (const char*)MenuList;
+ }
+ const char* getCommandTitleList()
+ {
+ return "";
+ }
+ void dispatch(const char* command, float* vMin, float* vMax, bool bSingleBrush) // message processing
+ {
+ if(string_equal(command, "About..."))
+ {
+ about_plugin_window();
+ }
+ if(string_equal(command, "ET-MapCoordinator"))
+ {
+ MapCoordinator();
+ }
+ }
+} // namespace
+
+class SunPlugModule : public TypeSystemRef
+{
+ _QERPluginTable m_plugin;
+public:
+ typedef _QERPluginTable Type;
+ STRING_CONSTANT(Name, "SunPlug");
+
+ SunPlugModule()
+ {
+ m_plugin.m_pfnQERPlug_Init = &SunPlug::init;
+ m_plugin.m_pfnQERPlug_GetName = &SunPlug::getName;
+ m_plugin.m_pfnQERPlug_GetCommandList = &SunPlug::getCommandList;
+ m_plugin.m_pfnQERPlug_GetCommandTitleList = &SunPlug::getCommandTitleList;
+ m_plugin.m_pfnQERPlug_Dispatch = &SunPlug::dispatch;
+ }
+ _QERPluginTable* getTable()
+ {
+ return &m_plugin;
+ }
+};
+
+typedef SingletonModule SingletonSunPlugModule;
+
+SingletonSunPlugModule g_SunPlugModule;
+
+
+extern "C" void RADIANT_DLLEXPORT Radiant_RegisterModules(ModuleServer& server)
+{
+ initialiseModule(server);
+
+ g_SunPlugModule.selfRegister();
+}
+
+// ************
+// ** my stuff **
+// ************
+
+// About dialog
+void about_plugin_window()
+{
+ GtkWidget *window, *vbox, *label, *button;
+
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); // create a window
+ gtk_window_set_transient_for(GTK_WINDOW(window), SunPlug::main_window); // make the window to stay in front of the main window
+ g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL); // connect the delete event
+ g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy), NULL); // connect the destroy event for the window
+ gtk_window_set_title(GTK_WINDOW(window), "About SunPlug"); // set the title of the window for the window
+ gtk_window_set_resizable(GTK_WINDOW(window), FALSE); // don't let the user resize the window
+ gtk_window_set_modal(GTK_WINDOW(window), TRUE); // force the user not to do something with the other windows
+ gtk_container_set_border_width(GTK_CONTAINER(window), 10); // set the border of the window
+
+ vbox = gtk_vbox_new(FALSE, 10); // create a box to arrange new objects vertically
+ gtk_container_add(GTK_CONTAINER(window), vbox); // add the box to the window
+
+ label = gtk_label_new("SunPlug v1.0 for GtkRadiant 1.5\nby Topsun"); // create a label
+ gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); // text align left
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 2); // insert the label in the box
+
+ button = gtk_button_new_with_label("OK"); // create a button with text
+ g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK (gtk_widget_destroy), window); // connect the click event to close the window
+ gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 2); // insert the button in the box
+
+ gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); // center the window on screen
+
+ gtk_widget_show_all(window); // show the window and all subelements
+}
+
+// get the current bounding box and return the optimal coordinates
+void GetOptimalCoordinates(AABB *levelBoundingBox)
+{
+ int half_width, half_heigth, center_x, center_y;
+
+ half_width = levelBoundingBox->extents.x();
+ half_heigth = levelBoundingBox->extents.y();
+ center_x = levelBoundingBox->origin.x();
+ center_y = levelBoundingBox->origin.y();
+
+ if (half_width > 175 || half_heigth > 175) // the square must be at least 350x350 units
+ {
+ // the wider side is the indicator for the square
+ if (half_width >= half_heigth)
+ {
+ minX = center_x - half_width;
+ maxX = center_x + half_width;
+ minY = center_y - half_width;
+ maxY = center_y + half_width;
+ } else {
+ minX = center_x - half_heigth;
+ maxX = center_x + half_heigth;
+ minY = center_y - half_heigth;
+ maxY = center_y + half_heigth;
+ }
+ } else {
+ minX = center_x - 175;
+ maxX = center_x + 175;
+ minY = center_y - 175;
+ maxY = center_y + 175;
+ }
+}
+
+// MapCoordinator dialog window
+void MapCoordinator()
+{
+ GtkWidget *window, *vbox, *table, *label, *spinnerMinX, *spinnerMinY, *spinnerMaxX, *spinnerMaxY, *button;
+ GtkAdjustment *spinner_adj_MinX, *spinner_adj_MinY, *spinner_adj_MaxX, *spinner_adj_MaxY;
+ Entity *theWorldspawn = NULL;
+ const char *buffer;
+ char line[20];
+
+ // in any case we need a window to show the user what to do
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); // create the window
+ gtk_window_set_transient_for(GTK_WINDOW(window), SunPlug::main_window); // make the window to stay in front of the main window
+ g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL); // connect the delete event for the window
+ g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy), NULL); // connect the destroy event for the window
+ gtk_window_set_title(GTK_WINDOW(window), "ET-MapCoordinator"); // set the title of the window for the window
+ gtk_window_set_resizable(GTK_WINDOW(window), FALSE); // don't let the user resize the window
+ gtk_window_set_modal(GTK_WINDOW(window), TRUE); // force the user not to do something with the other windows
+ gtk_container_set_border_width(GTK_CONTAINER(window), 10); // set the border of the window
+
+ vbox = gtk_vbox_new(FALSE, 10); // create a box to arrange new objects vertically
+ gtk_container_add(GTK_CONTAINER(window), vbox); // add the box to the window
+
+ scene::Path path = makeReference(GlobalSceneGraph().root()); // get the path to the root element of the graph
+ scene::Instance* instance = GlobalSceneGraph().find(path); // find the instance to the given path
+ AABB levelBoundingBox = instance->worldAABB(); // get the bounding box of the level
+
+ theWorldspawn = Scene_FindEntityByClass("worldspawn"); // find the entity worldspawn
+ if (theWorldspawn != 0) { // need to have a worldspawn otherwise setting a value crashes the radiant
+ // next two if's: get the current values of the mapcoords
+ buffer = theWorldspawn->getKeyValue("mapcoordsmins"); // upper left corner
+ if (strlen(buffer) > 0) {
+ strncpy(line, buffer, 19);
+ map_minX = atoi(strtok(line, " ")); // minimum of x value
+ map_minY = atoi(strtok(NULL, " ")); // maximum of y value
+ } else {
+ map_minX = 0;
+ map_minY = 0;
+ }
+ buffer = theWorldspawn->getKeyValue("mapcoordsmaxs"); // lower right corner
+ if (strlen(buffer) > 0) {
+ strncpy(line, buffer, 19);
+ map_maxX = atoi(strtok(line, " ")); // maximum of x value
+ map_maxY = atoi(strtok(NULL, " ")); // minimum of y value
+ } else {
+ map_maxX = 0;
+ map_maxY = 0;
+ }
+
+ globalOutputStream() << "SunPlug: calculating optimal coordinates\n"; // write to console that we are calculating the coordinates
+ GetOptimalCoordinates(&levelBoundingBox); // calculate optimal mapcoords with the dimensions of the level bounding box
+ globalOutputStream() << "SunPlug: adviced mapcoordsmins=" << minX << " " << maxY << "\n"; // console info about mapcoordsmins
+ globalOutputStream() << "SunPlug: adviced mapcoordsmaxs=" << maxX << " " << minY << "\n"; // console info about mapcoordsmaxs
+
+ spinner_adj_MinX = (GtkAdjustment *)gtk_adjustment_new(map_minX, -65536.0, 65536.0, 1.0, 5.0, 5.0); // create adjustment for value and range of minimum x value
+ spinner_adj_MinY = (GtkAdjustment *)gtk_adjustment_new(map_minY, -65536.0, 65536.0, 1.0, 5.0, 5.0); // create adjustment for value and range of minimum y value
+ spinner_adj_MaxX = (GtkAdjustment *)gtk_adjustment_new(map_maxX, -65536.0, 65536.0, 1.0, 5.0, 5.0); // create adjustment for value and range of maximum x value
+ spinner_adj_MaxY = (GtkAdjustment *)gtk_adjustment_new(map_maxY, -65536.0, 65536.0, 1.0, 5.0, 5.0); // create adjustment for value and range of maximum y value
+
+ button = gtk_button_new_with_label("Get optimal mapcoords"); // create button with text
+ g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(input_optimal), NULL); // connect button with callback function
+ gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 2); // insert button into vbox
+
+ gtk_box_pack_start(GTK_BOX(vbox), gtk_hseparator_new(), FALSE, FALSE, 2); // insert separator into vbox
+
+ table = gtk_table_new(4, 3, TRUE); // create table
+ gtk_table_set_row_spacings(GTK_TABLE(table), 8); // set row spacings
+ gtk_table_set_col_spacings(GTK_TABLE(table), 8); // set column spacings
+ gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 2); // insert table into vbox
+
+ label = gtk_label_new("x"); // create label
+ gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); // align text to the left side
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 0, 1); // insert label into table
+
+ label = gtk_label_new("y"); // create label
+ gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); // align text to the left side
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 2, 3, 0, 1); // insert label into table
+
+ label = gtk_label_new("mapcoordsmins"); // create label
+ gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); // align text to the left side
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2); // insert label into table
+
+ spinnerMinX = gtk_spin_button_new(spinner_adj_MinX, 1.0, 0); // create textbox wiht value spin, value and value range
+ gtk_table_attach_defaults(GTK_TABLE(table), spinnerMinX, 1, 2, 1, 2); // insert spinbox into table
+
+ spinnerMinY = gtk_spin_button_new(spinner_adj_MinY, 1.0, 0); // create textbox wiht value spin, value and value range
+ gtk_table_attach_defaults(GTK_TABLE(table), spinnerMinY, 2, 3, 1, 2); // insert spinbox into table
+
+ label = gtk_label_new("mapcoordsmaxs"); // create label
+ gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); // align text to the left side
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3); // insert label into table
+
+ spinnerMaxX = gtk_spin_button_new(spinner_adj_MaxX, 1.0, 0); // create textbox wiht value spin, value and value range
+ gtk_table_attach_defaults(GTK_TABLE(table), spinnerMaxX, 1, 2, 2, 3); // insert spinbox into table
+
+ spinnerMaxY = gtk_spin_button_new(spinner_adj_MaxY, 1.0, 0); // create textbox wiht value spin, value and value range
+ gtk_table_attach_defaults(GTK_TABLE(table), spinnerMaxY, 2, 3, 2, 3); // insert spinbox into table
+
+ // put the references to the spinboxes and the worldspawn into the global exchange
+ msp.spinner1 = GTK_SPIN_BUTTON(spinnerMinX);
+ msp.spinner2 = GTK_SPIN_BUTTON(spinnerMinY);
+ msp.spinner3 = GTK_SPIN_BUTTON(spinnerMaxX);
+ msp.spinner4 = GTK_SPIN_BUTTON(spinnerMaxY);
+ msp.worldspawn = theWorldspawn;
+
+ button = gtk_button_new_with_label("Set"); // create button with text
+ g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(set_coordinates), NULL); // connect button with callback function
+ gtk_table_attach_defaults(GTK_TABLE(table), button, 1, 2, 3, 4); // insert button into table
+
+ button = gtk_button_new_with_label("Cancel"); // create button with text
+ g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(close_window), NULL); // connect button with callback function
+ gtk_table_attach_defaults(GTK_TABLE(table), button, 2, 3, 3, 4); // insert button into table
+ } else {
+ globalOutputStream() << "SunPlug: no worldspawn found!\n"; // output error to console
+
+ label = gtk_label_new("ERROR: No worldspawn was found in the map!\nIn order to use this tool the map must have at least one brush in the worldspawn. "); // create a label
+ gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); // text align left
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 2); // insert the label in the box
+
+ button = gtk_button_new_with_label("OK"); // create a button with text
+ g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(close_window), NULL); // connect the click event to close the window
+ gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 2); // insert the button in the box
+ }
+
+ gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); // center the window
+ gtk_widget_show_all(window); // show the window and all subelements
+}
\ No newline at end of file
diff --git a/contrib/sunplug/sunplug.def b/contrib/sunplug/sunplug.def
new file mode 100644
index 00000000..6d260776
--- /dev/null
+++ b/contrib/sunplug/sunplug.def
@@ -0,0 +1,7 @@
+; sunplug.def : Declares the module parameters for the DLL.
+
+LIBRARY "SUNPLUG"
+
+EXPORTS
+ ; Explicit exports can go here
+ Radiant_RegisterModules @1
diff --git a/contrib/sunplug/sunplug.h b/contrib/sunplug/sunplug.h
new file mode 100644
index 00000000..9649a743
--- /dev/null
+++ b/contrib/sunplug/sunplug.h
@@ -0,0 +1,25 @@
+/*
+Sunplug plugin for GtkRadiant
+Copyright (C) 2004 Topsun
+Thanks to SPoG for help!
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef _PLUGIN_SUNPLUG_
+ #define _PLUGIN_SUNPLUG_
+
+
+#endif // _PLUGIN_SUNPLUG_
diff --git a/contrib/sunplug/sunplug.vcproj b/contrib/sunplug/sunplug.vcproj
new file mode 100644
index 00000000..2d6fce90
--- /dev/null
+++ b/contrib/sunplug/sunplug.vcproj
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+