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