transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / plugins / model / miscmodel.cpp
1 /*\r
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.\r
4 \r
5 This file is part of GtkRadiant.\r
6 \r
7 GtkRadiant is free software; you can redistribute it and/or modify\r
8 it under the terms of the GNU General Public License as published by\r
9 the Free Software Foundation; either version 2 of the License, or\r
10 (at your option) any later version.\r
11 \r
12 GtkRadiant is distributed in the hope that it will be useful,\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
15 GNU General Public License for more details.\r
16 \r
17 You should have received a copy of the GNU General Public License\r
18 along with GtkRadiant; if not, write to the Free Software\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
20 */\r
21 \r
22 #include <stdlib.h>\r
23 \r
24 #include "entitymodel.h"\r
25 \r
26 extern CModelManager g_model_cache;\r
27 \r
28 //\r
29 // CEntityMiscModel implementation\r
30 //\r
31 \r
32 CEntityMiscModel::CEntityMiscModel ()\r
33 {\r
34   refCount = 1;\r
35   m_name = NULL;\r
36   m_model = NULL;\r
37         m_entity = NULL;\r
38   m_frame = 0;\r
39   m_remaps = g_ptr_array_new ();\r
40   m_shaders = g_ptr_array_new ();\r
41   VectorSet(m_translate, 0,0,0);\r
42   VectorSet(m_euler, 0,0,0);\r
43   VectorSet(m_scale, 1,1,1);\r
44   VectorSet(m_pivot, 0,0,0);\r
45   m4x4_identity(m_transform);\r
46   m4x4_identity(m_inverse_transform);\r
47 }\r
48 \r
49 typedef struct remap_s {\r
50   char m_key[64];\r
51   char m_remapbuff[64+1024];\r
52   char *m_remap[2];\r
53 } remap_t;\r
54 \r
55 CEntityMiscModel::~CEntityMiscModel ()\r
56 {\r
57   unsigned int i;\r
58 \r
59   if(m_name && *m_name != '\0') {\r
60     if( !g_model_cache.DeleteByNameAndFrame(m_name,m_frame) && m_model )\r
61       m_model->RemoveParent( this );\r
62     m_model = NULL;\r
63     delete [] m_name;\r
64   }\r
65 \r
66   for( i = 0; i < m_remaps->len; i++ )\r
67     delete (remap_t*)m_remaps->pdata[i];\r
68   g_ptr_array_free(m_remaps, FALSE);\r
69 \r
70   for( i = 0; i < m_shaders->len; i++ )\r
71   {\r
72     (*(IShader**)m_shaders->pdata[i])->DecRef();\r
73     delete (IShader**)m_shaders->pdata[i];\r
74   }\r
75   g_ptr_array_free(m_shaders, FALSE);\r
76 \r
77   if(m_entity) {\r
78     // This might be just an evasion of the actual problem\r
79     m_entity->model.pRender = NULL;\r
80     m_entity->model.pSelect = NULL;\r
81     m_entity->model.pEdit = NULL;\r
82   }\r
83 }\r
84 \r
85 // IRender\r
86 \r
87 void CEntityMiscModel::Draw(int state, int rflags) const\r
88 {\r
89   m4x4_t matrix;\r
90   vec3_t pivot;\r
91  \r
92   memcpy(matrix, m_transform, sizeof(m4x4_t));\r
93   m4x4_transpose(matrix);\r
94 \r
95   VectorAdd(m_pivot, m_translate, pivot);\r
96   pivot_draw(pivot);\r
97   \r
98   // push the current modelview matrix\r
99   // FIXME: put in a check for stack recursion depth..\r
100   // or avoid recursion of opengl matrix stack\r
101   g_QglTable.m_pfn_qglPushMatrix();\r
102   // apply the parent-to-local transform\r
103   g_QglTable.m_pfn_qglMultMatrixf(matrix);\r
104 \r
105   // draw children\r
106   if(m_model)\r
107     m_model->Draw(state, m_shaders, rflags);\r
108  \r
109   g_QglTable.m_pfn_qglPopMatrix();\r
110 }\r
111 \r
112 // ISelect\r
113 \r
114 bool CEntityMiscModel::TestRay(const ray_t *ray, vec_t *dist) const\r
115 {\r
116   vec_t dist_start = *dist;\r
117   vec_t dist_local = *dist;\r
118         ray_t ray_local = *ray;\r
119 \r
120   if (!aabb_intersect_ray(&m_BBox, &ray_local, &dist_local))\r
121     return false;\r
122 \r
123   if(m_model){\r
124     ray_transform(&ray_local, m_inverse_transform);\r
125     dist_local = dist_start;\r
126     if(m_model->TestRay(&ray_local, &dist_local))\r
127         *dist = dist_local;\r
128   } else *dist = dist_local;\r
129 \r
130   return *dist < dist_start;\r
131 }\r
132 \r
133 \r
134 //IEdit\r
135 \r
136 void CEntityMiscModel::Translate(const vec3_t translation)\r
137 {\r
138   VectorIncrement(translation, m_translate);\r
139   UpdateCachedData();\r
140 }\r
141 \r
142 void CEntityMiscModel::Rotate(const vec3_t pivot, const vec3_t rotation)\r
143 {\r
144   m4x4_t rotation_matrix;\r
145 \r
146   m4x4_identity(rotation_matrix);\r
147   m4x4_pivoted_rotate_by_vec3(rotation_matrix, rotation, pivot);\r
148   m4x4_transform_point(rotation_matrix, m_translate);\r
149 \r
150   VectorIncrement(rotation, m_euler);\r
151 \r
152   UpdateCachedData();\r
153 }\r
154 \r
155 void CEntityMiscModel::OnKeyChanged(entity_t *e, const char *key)\r
156 {\r
157   const char *value;\r
158 \r
159   // FIXME: keys are case-sensitive?\r
160         \r
161         m_entity = e;\r
162 \r
163   if(strcmp(key,"model") == 0)\r
164     SetName(ValueForKey(e,"model"));\r
165   else if(strcmp(key,"_frame") == 0)\r
166     SetFrame(IntForKey(e,"_frame"));\r
167   else if(strcmp(key,"angle") == 0 || strcmp(key,"angles") == 0)\r
168   {\r
169     VectorSet(m_euler, 0.f, 0.f, 0.f);\r
170     m_euler[2] = FloatForKey(e,"angle");\r
171     value = ValueForKey(e,"angles");\r
172     if (value[0] != '\0')\r
173       sscanf (value, "%f %f %f", &m_euler[0], &m_euler[2], &m_euler[1]);\r
174     UpdateCachedData();\r
175   }\r
176   else if(strcmp(key,"modelscale") == 0 || strcmp(key,"modelscale_vec") == 0)\r
177   {\r
178     VectorSet(m_scale, 1.f, 1.f, 1.f);\r
179     value = ValueForKey(e,"modelscale");\r
180     if (value[0] != '\0')\r
181     {\r
182       float f = atof(value);\r
183       if( f != 0 )\r
184         VectorSet(m_scale, f, f, f);\r
185       else\r
186         Sys_FPrintf(SYS_WRN, "WARNING: ignoring 0 modelscale key\n");\r
187     }\r
188     value = ValueForKey(e,"modelscale_vec");\r
189     if (value[0] != '\0')\r
190     {\r
191       sscanf (value, "%f %f %f", &m_scale[0], &m_scale[1], &m_scale[2]);\r
192       if (m_scale[0] == 0.0 && m_scale[1] == 0.0 && m_scale[2] == 0.0)\r
193       {\r
194         VectorSet(m_scale, 1,1,1);\r
195         Sys_FPrintf(SYS_WRN, "WARNING: ignoring 0 0 0 modelscale_vec key\n");\r
196       }\r
197     }\r
198     UpdateCachedData();\r
199   }\r
200   else if(strcmp(key,"origin") == 0)\r
201   {\r
202     value = ValueForKey(e,"origin");\r
203     sscanf(value, "%f %f %f", &m_translate[0], &m_translate[1], &m_translate[2]); \r
204     UpdateCachedData();\r
205   }\r
206   else if(strncmp(key,"_remap",6) == 0)\r
207   {\r
208     unsigned int i;\r
209     remap_t *pRemap;\r
210     char *ch;\r
211 \r
212     value = ValueForKey(e,key);\r
213 \r
214     for(i=0; i<m_remaps->len; i++)\r
215     {\r
216       pRemap = (remap_t*)m_remaps->pdata[i];\r
217       if(strcmp(key,pRemap->m_key) == 0)\r
218         break;\r
219     }\r
220 \r
221     if( i == m_remaps->len )\r
222     {\r
223       if( value[0] == '\0' )\r
224         return;\r
225 \r
226       pRemap = new remap_t;\r
227       g_ptr_array_add(m_remaps, pRemap);\r
228     }\r
229     else if( value[0] == '\0' )\r
230     {\r
231       g_ptr_array_remove_index_fast(m_remaps, i);\r
232       delete pRemap;\r
233 \r
234       UpdateShaders();\r
235       return;\r
236     }\r
237 \r
238     strncpy(pRemap->m_remapbuff,value,sizeof(pRemap->m_remapbuff));\r
239     strncpy(pRemap->m_key,key,sizeof(pRemap->m_key));\r
240 \r
241     pRemap->m_remap[0] = ch = pRemap->m_remapbuff;\r
242 \r
243     while( *ch && *ch != ';' )\r
244       ch++;\r
245 \r
246     if( *ch == '\0' )\r
247     {    \r
248       // bad remap\r
249       Sys_FPrintf(SYS_WRN, "WARNING: Shader _remap key found in misc_model without a ; character\n" );\r
250       g_ptr_array_remove_index_fast(m_remaps, i);\r
251       delete pRemap;\r
252       return;\r
253     }\r
254     else\r
255     {\r
256       *ch = '\0';\r
257       pRemap->m_remap[1] = ch + 1;\r
258     }\r
259 \r
260     UpdateShaders();\r
261   }\r
262 }\r
263 \r
264 //\r
265 // CEntityMiscModel\r
266 //\r
267 \r
268 // private:\r
269 \r
270 void CEntityMiscModel::SetName(const char *name)\r
271 {\r
272   if(m_name && *m_name != '\0') {\r
273     if(strcmp(m_name, name) == 0)\r
274       return;\r
275     if( !g_model_cache.DeleteByNameAndFrame(m_name,m_frame) && m_model )\r
276       m_model->RemoveParent( this );\r
277     delete [] m_name;\r
278   }\r
279 \r
280   m_model = NULL;\r
281   m_name = new char[strlen(name)+1];\r
282   strcpy(m_name,name);\r
283 \r
284   if(*m_name != '\0') {\r
285     m_model = g_model_cache.GetByNameAndFrame(m_name, m_frame);\r
286     m_model->AddParent( this );\r
287   }\r
288 \r
289   UpdateCachedData();\r
290   UpdateShaders();\r
291 }\r
292 \r
293 void CEntityMiscModel::SetFrame(const int frame)\r
294 {\r
295   if( m_frame == frame )\r
296     return;\r
297 \r
298   if(m_name && *m_name != '\0') {\r
299     if( !g_model_cache.DeleteByNameAndFrame(m_name,m_frame) && m_model )\r
300       m_model->RemoveParent( this );\r
301   }\r
302 \r
303   m_model = NULL;\r
304 \r
305   m_frame = frame;\r
306 \r
307   if(*m_name != '\0') {\r
308     m_model = g_model_cache.GetByNameAndFrame(m_name, m_frame);\r
309     m_model->AddParent( this );\r
310   }\r
311 \r
312   UpdateCachedData();\r
313 }\r
314 \r
315 void CEntityMiscModel::UpdateCachedData()\r
316 {\r
317   aabb_t aabb_temp;\r
318   bbox_t bbox_temp;\r
319 \r
320   m4x4_identity(m_transform);\r
321   m4x4_pivoted_transform_by_vec3(m_transform, m_translate, m_euler, m_scale, m_pivot);\r
322   memcpy(m_inverse_transform, m_transform, sizeof(m4x4_t));\r
323   if(m4x4_invert(m_inverse_transform) == 1) {\r
324     Sys_Printf("ERROR: Singular Matrix, cannot invert");\r
325   }\r
326 \r
327   aabb_clear(&aabb_temp);\r
328 \r
329   if(m_model)\r
330     aabb_extend_by_aabb(&aabb_temp, m_model->GetAABB());\r
331   else\r
332         {\r
333                 if (m_entity->eclass)\r
334                         VectorSet(aabb_temp.extents, m_entity->eclass->maxs[0], m_entity->eclass->maxs[1], m_entity->eclass->maxs[2]);\r
335                 else\r
336                         VectorSet(aabb_temp.extents, 8, 8, 8);\r
337         }\r
338 \r
339   // create an oriented BBox in world-space\r
340   bbox_for_oriented_aabb(&bbox_temp, &aabb_temp, m_transform, m_euler, m_scale);\r
341   // create an axis aligned bbox in world-space\r
342   aabb_for_bbox(&m_BBox, &bbox_temp);\r
343 \r
344   aabb_update_radius(&m_BBox);\r
345 }\r
346 \r
347 void CEntityMiscModel::UpdateShaders()\r
348 {\r
349   unsigned int i, j, numSurfaces;\r
350   remap_t *pRemap, *pGlobRemap = NULL;\r
351   char *surfShaderName;\r
352   IShader **pShader;\r
353 \r
354   if( !m_model )\r
355   {\r
356     if( m_shaders->len )\r
357     {\r
358       // free our shaders\r
359       for( i = 0; i < m_shaders->len; i++ )\r
360       {\r
361         g_ptr_array_remove_index_fast(m_shaders, i);\r
362         (*(IShader**)m_shaders->pdata[i])->DecRef();\r
363         delete (IShader**)m_shaders->pdata[i];\r
364       }\r
365     }\r
366     return;\r
367   }\r
368   \r
369   numSurfaces = m_model->GetNumSurfaces();\r
370 \r
371   if( numSurfaces < m_shaders->len )\r
372   {\r
373     // free unneeded shader pointers\r
374     for( i = m_shaders->len - 1; i >= numSurfaces; i-- )\r
375     {\r
376       g_ptr_array_remove_index_fast(m_shaders, i);\r
377       (*(IShader**)m_shaders->pdata[i])->DecRef();\r
378       delete (IShader**)m_shaders->pdata[i];\r
379     }\r
380   }\r
381 \r
382   // now go through our surface and find our shaders, remap if needed\r
383   for( j = 0; j < numSurfaces; j++ )\r
384   {\r
385     surfShaderName = m_model->GetShaderNameForSurface(j);\r
386 \r
387     if( j < m_shaders->len )\r
388     {\r
389       pShader = (IShader **)m_shaders->pdata[j];\r
390     }\r
391     else\r
392     {\r
393       pShader = new (IShader *);\r
394       *pShader = NULL;\r
395       g_ptr_array_add(m_shaders, pShader);\r
396     }\r
397 \r
398     if( m_remaps->len )\r
399     {\r
400       for( i = 0; i < m_remaps->len; i++ )\r
401       {\r
402         pRemap = (remap_t*)m_remaps->pdata[i];\r
403         if( stricmp(pRemap->m_remap[0],surfShaderName) == 0 )\r
404         {\r
405           // only do the shader lookups if really needed\r
406           if( !(*pShader) || stricmp(pRemap->m_remap[1],(*pShader)->getName()) )\r
407           {\r
408             if( *pShader )\r
409               (*pShader)->DecRef();\r
410             *pShader = QERApp_Shader_ForName(pRemap->m_remap[1]);\r
411           }\r
412 \r
413           pGlobRemap = NULL;\r
414           break;\r
415         }\r
416         else if( pRemap->m_remap[0][0] == '*' && pRemap->m_remap[0][1] == '\0' )\r
417           pGlobRemap = pRemap;\r
418       }\r
419 \r
420       if( pGlobRemap )\r
421       {\r
422         if( !(*pShader) || stricmp(pGlobRemap->m_remap[1],(*pShader)->getName()) )\r
423         {\r
424           if( *pShader )\r
425             (*pShader)->DecRef();\r
426           *pShader = QERApp_Shader_ForName(pGlobRemap->m_remap[1]);\r
427         }\r
428       }\r
429       else if( i == m_remaps->len )\r
430       {\r
431         // Back to the default one, if needed\r
432         if( !(*pShader) || (stricmp(surfShaderName,(*pShader)->getName()) && !(surfShaderName[0] == '\0')) )\r
433         {\r
434           if( *pShader )\r
435             (*pShader)->DecRef();\r
436           *pShader = QERApp_Shader_ForName(surfShaderName);\r
437         }\r
438       }\r
439     }\r
440     else\r
441     {\r
442       // Model specified shader, if needed\r
443       if( !(*pShader) || (stricmp(surfShaderName,(*pShader)->getName()) && !(surfShaderName[0] == '\0')) )\r
444       {\r
445         if( *pShader )\r
446           (*pShader)->DecRef();\r
447         *pShader = QERApp_Shader_ForName(surfShaderName);\r
448       }\r
449     }\r
450   }\r
451 }\r