uncrustify! now the code is only ugly on the *inside*
[xonotic/netradiant.git] / plugins / spritemodel / plugin.cpp
1 /*
2    Copyright (C) 1999-2007 id Software, Inc. and contributors.
3    For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 //
23 // Sprite Model Plugin
24 //
25 // Code by Hydra aka Dominic Clifton
26 //
27 // Based on MD3Model source code by SPoG
28 //
29
30 /*
31     Overview
32     ========
33
34
35     Why ?
36     -----
37
38     It allows the user to see a graphical representation of the entity in the 3D view (maybe 2D views later) where the entity would just otherwise be a non-descriptive coloured box.
39
40     It is designed to be used with the entity view set to WireFrame (as the sprite images are rendered in the middle of the entity's bbox).
41
42     How ?
43     -----
44
45     Implemented as a model module, without any ISelect stuff.
46
47     For an entity to use an image (instead of a model) you just update the entity defintion file so that the eclass_t's modelpath is filled in with a relative path and filename of an image file.
48
49     e.g:
50
51       baseq3/scripts/entities.def
52       ===========================
53
54    \/\*QUAKED ammo_bfg (.3 .3 1) (-16 -16 -16) (16 16 16) SUSPENDED
55       ...
56       -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
57       model="sprites/powerups/ammo/bfgam.bmp"\*\/
58
59
60       valve/scripts/halflife.fgd
61       ==========================
62
63       @PointClass iconsprite("sprites/lightbulb.spr") base(Target, Targetname, Light) = light : "Invisible   lightsource"
64       [
65               ...
66       ]
67
68     What image formats are supported ?
69     ----------------------------------
70
71     This module can load any image format that there is an active image module for.  For q3 this would be bmp, tga and jpg.  For Half-Life this would be hlw and spr.
72
73     Version History
74     ===============
75
76     v0.1 - 27/May/2002
77       - Created an inital implementation of a sprite model plugin.
78         According to the powers that be, it seems creating a model
79         plugin is hackish.
80         It works ok, but there is no way to attach models (sprites if you will)
81         to non-fixedsize entities (like func_bombtarget)
82         Also, I can't get the alpha map stuff right so I had to invert the alpha
83         mask in the spr loader so that 0xff = not drawn pixel.
84
85     v0.2 - 10/March/2003
86       - Updated to coincide with Radiant 1.3.5 test builds.  Also, I made sure it worked
87         under quake3 and it does.
88
89     v0.3 - 10/March/2003
90       - Added about box.
91
92     ToDo
93     ====
94
95  * make sprites always face the camera (is this done in camwindow.cpp ?)
96       but only if the entity model doesn't have "angle" keys.  At the moment
97       it's better to rotate the model with the angles.
98
99  * maybe add an option to scale the sprites in the prefs ?
100
101  * maybe convert to a new kind of class not based on model.
102
103  * allow sprites on non-fixedsize ents
104
105  * fix reversed alpha map in spr loader
106       -> is this actually broken?
107
108  * allow an entity to have multiple models (e.g .md3 and a sprite model)
109       and allow the user to toggle either models on or off.
110
111  * dynamically add the api's depending on what image loading modules are
112       supported by radiant.
113       Currently, we hard code to the list in "supportedmodelformats" (see below)
114       but, all these extensions are stripped when the actual image is loaded.
115       current the bit of code that decided what model api to use needs reworking
116       as it decides by looking at the extension of the model name, when in fact
117       we don't even need an extension.
118
119       Previously the code fell though to use this model as the default model
120       plugin, but that also has issues.
121
122       what it means is, in the .def files you must specify an image filename
123       that has one of the extensions listed below, but in actual fact radiant
124       will use any available image module to load the image.
125
126
127       e.g. you could use a model name of "sprites/target_speaker.tga" and have
128       a file called sprites/target_speaker.png and it would be correctly loaded
129       even if it not listed below in "supportedmodelformats".
130
131       So, currently in the .def files you can just use the name
132       "sprites/target_speaker.spr" and it will load the file
133       from "sprites/target_speaker.*" which is what I propose anyone creating image sets for Q3/Wolf/etc does.
134  */
135
136 #include "plugin.h"
137
138 // =============================================================================
139 // Globals
140
141 // function tables
142 _QERFuncTable_1 g_FuncTable;
143 _QERQglTable g_QglTable;
144 _QERShadersTable g_ShadersTable;
145
146 // =============================================================================
147 // plugin implementation
148
149 static const char *PLUGIN_NAME = "Sprite Model loading module";
150
151 static const char *PLUGIN_COMMANDS = "About...";
152
153 static const char *PLUGIN_ABOUT = "Sprite Model loading module v0.2 for GTKRadiant\n\n"
154                                                                   "By Hydra!";
155
156 char *supportedmodelformats[] = {"spr","bmp","tga","jpg","hlw",NULL}; // NULL is list delimiter
157
158 static void add_model_apis( CSynapseClient& client ){
159         char **ext;
160         for ( ext = supportedmodelformats; *ext != NULL; ext++ )
161         {
162                 client.AddAPI( MODEL_MAJOR, *ext, sizeof( _QERPlugModelTable ) );
163         }
164 }
165
166 static bool model_is_supported( const char* extension ){
167         char **ext;
168         for ( ext = supportedmodelformats; *ext != NULL; ext++ )
169         {
170                 if ( stricmp( extension,*ext ) == 0 ) {
171                         return true;
172                 }
173         }
174         return false;
175 }
176
177 void init_filetypes(){
178         char **ext;
179         for ( ext = supportedmodelformats; *ext != NULL; ext++ )
180         {
181                 GetFileTypeRegistry()->addType( MODEL_MAJOR, filetype_t( "sprite", *ext ) );
182         }
183 }
184
185 extern "C" const char* QERPlug_Init( void *hApp, void* pMainWidget ){
186         init_filetypes(); // see todo list above.
187         return (char *) PLUGIN_NAME;
188 }
189
190 extern "C" const char* QERPlug_GetName(){
191         return (char *) PLUGIN_NAME;
192 }
193
194 extern "C" const char* QERPlug_GetCommandList(){
195         return (char *) PLUGIN_COMMANDS;
196 }
197
198 extern "C" void QERPlug_Dispatch( const char *p, vec3_t vMin, vec3_t vMax, bool bSingleBrush ){
199         // NOTE: this never happens in a module
200         if ( !strcmp( p, "About..." ) ) {
201                 g_FuncTable.m_pfnMessageBox( NULL, PLUGIN_ABOUT, "About", MB_OK, NULL );
202         }
203 }
204
205 // =============================================================================
206 // SYNAPSE
207
208 CSynapseServer* g_pSynapseServer = NULL;
209 CSynapseClientModel g_SynapseClient;
210
211 #if __GNUC__ >= 4
212 #pragma GCC visibility push(default)
213 #endif
214 extern "C" CSynapseClient * SYNAPSE_DLL_EXPORT Synapse_EnumerateInterfaces( const char *version, CSynapseServer *pServer ) {
215 #if __GNUC__ >= 4
216 #pragma GCC visibility pop
217 #endif
218         if ( strcmp( version, SYNAPSE_VERSION ) ) {
219                 Syn_Printf( "ERROR: synapse API version mismatch: should be '" SYNAPSE_VERSION "', got '%s'\n", version );
220                 return NULL;
221         }
222         g_pSynapseServer = pServer;
223         g_pSynapseServer->IncRef();
224         Set_Syn_Printf( g_pSynapseServer->Get_Syn_Printf() );
225
226         add_model_apis( g_SynapseClient ); // see todo list above.
227
228         g_SynapseClient.AddAPI( PLUGIN_MAJOR, "sprite", sizeof( _QERPluginTable ) );
229         g_SynapseClient.AddAPI( RADIANT_MAJOR, NULL, sizeof( g_FuncTable ), SYN_REQUIRE, &g_FuncTable );
230         g_SynapseClient.AddAPI( QGL_MAJOR, NULL, sizeof( g_QglTable ), SYN_REQUIRE, &g_QglTable );
231         g_SynapseClient.AddAPI( SHADERS_MAJOR, "*", sizeof( g_ShadersTable ), SYN_REQUIRE, &g_ShadersTable );
232
233         return &g_SynapseClient;
234 }
235
236 bool CSynapseClientModel::RequestAPI( APIDescriptor_t *pAPI ){
237         if ( !strcmp( pAPI->major_name, MODEL_MAJOR ) ) {
238                 _QERPlugModelTable* pTable = static_cast<_QERPlugModelTable*>( pAPI->mpTable );
239
240                 if ( model_is_supported( pAPI->minor_name ) ) { // see todo list above.
241                         pTable->m_pfnLoadModel = &LoadSpriteModel;
242                         return true;
243                 }
244         }
245         else if ( !strcmp( pAPI->major_name, PLUGIN_MAJOR ) ) {
246                 _QERPluginTable* pTable = static_cast<_QERPluginTable*>( pAPI->mpTable );
247
248                 pTable->m_pfnQERPlug_Init = QERPlug_Init;
249                 pTable->m_pfnQERPlug_GetName = QERPlug_GetName;
250                 pTable->m_pfnQERPlug_GetCommandList = QERPlug_GetCommandList;
251                 pTable->m_pfnQERPlug_Dispatch = QERPlug_Dispatch;
252                 return true;
253         }
254
255         Syn_Printf( "ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo() );
256         return false;
257 }
258
259 #include "version.h"
260
261 const char* CSynapseClientModel::GetInfo(){
262         return "Sprite Model module built " __DATE__ " " RADIANT_VERSION;
263 }
264
265 const char* CSynapseClientModel::GetName(){
266         return "sprite";
267 }