2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
\r
5 This file is part of GtkRadiant.
\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
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
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
24 #include "entitymodel.h"
\r
26 extern CModelManager g_model_cache;
\r
29 // CEntityMiscModel implementation
\r
32 CEntityMiscModel::CEntityMiscModel ()
\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
49 typedef struct remap_s {
\r
51 char m_remapbuff[64+1024];
\r
55 CEntityMiscModel::~CEntityMiscModel ()
\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
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
70 for( i = 0; i < m_shaders->len; i++ )
\r
72 (*(IShader**)m_shaders->pdata[i])->DecRef();
\r
73 delete (IShader**)m_shaders->pdata[i];
\r
75 g_ptr_array_free(m_shaders, FALSE);
\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
87 void CEntityMiscModel::Draw(int state, int rflags) const
\r
92 memcpy(matrix, m_transform, sizeof(m4x4_t));
\r
93 m4x4_transpose(matrix);
\r
95 VectorAdd(m_pivot, m_translate, pivot);
\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
107 m_model->Draw(state, m_shaders, rflags);
\r
109 g_QglTable.m_pfn_qglPopMatrix();
\r
114 bool CEntityMiscModel::TestRay(const ray_t *ray, vec_t *dist) const
\r
116 vec_t dist_start = *dist;
\r
117 vec_t dist_local = *dist;
\r
118 ray_t ray_local = *ray;
\r
120 if (!aabb_intersect_ray(&m_BBox, &ray_local, &dist_local))
\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
130 return *dist < dist_start;
\r
136 void CEntityMiscModel::Translate(const vec3_t translation)
\r
138 VectorIncrement(translation, m_translate);
\r
139 UpdateCachedData();
\r
142 void CEntityMiscModel::Rotate(const vec3_t pivot, const vec3_t rotation)
\r
144 m4x4_t rotation_matrix;
\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
150 VectorIncrement(rotation, m_euler);
\r
152 UpdateCachedData();
\r
155 void CEntityMiscModel::OnKeyChanged(entity_t *e, const char *key)
\r
159 // FIXME: keys are case-sensitive?
\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
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
176 else if(strcmp(key,"modelscale") == 0 || strcmp(key,"modelscale_vec") == 0)
\r
178 VectorSet(m_scale, 1.f, 1.f, 1.f);
\r
179 value = ValueForKey(e,"modelscale");
\r
180 if (value[0] != '\0')
\r
182 float f = atof(value);
\r
184 VectorSet(m_scale, f, f, f);
\r
186 Sys_FPrintf(SYS_WRN, "WARNING: ignoring 0 modelscale key\n");
\r
188 value = ValueForKey(e,"modelscale_vec");
\r
189 if (value[0] != '\0')
\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
194 VectorSet(m_scale, 1,1,1);
\r
195 Sys_FPrintf(SYS_WRN, "WARNING: ignoring 0 0 0 modelscale_vec key\n");
\r
198 UpdateCachedData();
\r
200 else if(strcmp(key,"origin") == 0)
\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
206 else if(strncmp(key,"_remap",6) == 0)
\r
212 value = ValueForKey(e,key);
\r
214 for(i=0; i<m_remaps->len; i++)
\r
216 pRemap = (remap_t*)m_remaps->pdata[i];
\r
217 if(strcmp(key,pRemap->m_key) == 0)
\r
221 if( i == m_remaps->len )
\r
223 if( value[0] == '\0' )
\r
226 pRemap = new remap_t;
\r
227 g_ptr_array_add(m_remaps, pRemap);
\r
229 else if( value[0] == '\0' )
\r
231 g_ptr_array_remove_index_fast(m_remaps, i);
\r
238 strncpy(pRemap->m_remapbuff,value,sizeof(pRemap->m_remapbuff));
\r
239 strncpy(pRemap->m_key,key,sizeof(pRemap->m_key));
\r
241 pRemap->m_remap[0] = ch = pRemap->m_remapbuff;
\r
243 while( *ch && *ch != ';' )
\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
257 pRemap->m_remap[1] = ch + 1;
\r
265 // CEntityMiscModel
\r
270 void CEntityMiscModel::SetName(const char *name)
\r
272 if(m_name && *m_name != '\0') {
\r
273 if(strcmp(m_name, name) == 0)
\r
275 if( !g_model_cache.DeleteByNameAndFrame(m_name,m_frame) && m_model )
\r
276 m_model->RemoveParent( this );
\r
281 m_name = new char[strlen(name)+1];
\r
282 strcpy(m_name,name);
\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
289 UpdateCachedData();
\r
293 void CEntityMiscModel::SetFrame(const int frame)
\r
295 if( m_frame == frame )
\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
307 if(*m_name != '\0') {
\r
308 m_model = g_model_cache.GetByNameAndFrame(m_name, m_frame);
\r
309 m_model->AddParent( this );
\r
312 UpdateCachedData();
\r
315 void CEntityMiscModel::UpdateCachedData()
\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
327 aabb_clear(&aabb_temp);
\r
330 aabb_extend_by_aabb(&aabb_temp, m_model->GetAABB());
\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
336 VectorSet(aabb_temp.extents, 8, 8, 8);
\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
344 aabb_update_radius(&m_BBox);
\r
347 void CEntityMiscModel::UpdateShaders()
\r
349 unsigned int i, j, numSurfaces;
\r
350 remap_t *pRemap, *pGlobRemap = NULL;
\r
351 char *surfShaderName;
\r
356 if( m_shaders->len )
\r
358 // free our shaders
\r
359 for( i = 0; i < m_shaders->len; i++ )
\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
369 numSurfaces = m_model->GetNumSurfaces();
\r
371 if( numSurfaces < m_shaders->len )
\r
373 // free unneeded shader pointers
\r
374 for( i = m_shaders->len - 1; i >= numSurfaces; i-- )
\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
382 // now go through our surface and find our shaders, remap if needed
\r
383 for( j = 0; j < numSurfaces; j++ )
\r
385 surfShaderName = m_model->GetShaderNameForSurface(j);
\r
387 if( j < m_shaders->len )
\r
389 pShader = (IShader **)m_shaders->pdata[j];
\r
393 pShader = new (IShader *);
\r
395 g_ptr_array_add(m_shaders, pShader);
\r
398 if( m_remaps->len )
\r
400 for( i = 0; i < m_remaps->len; i++ )
\r
402 pRemap = (remap_t*)m_remaps->pdata[i];
\r
403 if( stricmp(pRemap->m_remap[0],surfShaderName) == 0 )
\r
405 // only do the shader lookups if really needed
\r
406 if( !(*pShader) || stricmp(pRemap->m_remap[1],(*pShader)->getName()) )
\r
409 (*pShader)->DecRef();
\r
410 *pShader = QERApp_Shader_ForName(pRemap->m_remap[1]);
\r
416 else if( pRemap->m_remap[0][0] == '*' && pRemap->m_remap[0][1] == '\0' )
\r
417 pGlobRemap = pRemap;
\r
422 if( !(*pShader) || stricmp(pGlobRemap->m_remap[1],(*pShader)->getName()) )
\r
425 (*pShader)->DecRef();
\r
426 *pShader = QERApp_Shader_ForName(pGlobRemap->m_remap[1]);
\r
429 else if( i == m_remaps->len )
\r
431 // Back to the default one, if needed
\r
432 if( !(*pShader) || (stricmp(surfShaderName,(*pShader)->getName()) && !(surfShaderName[0] == '\0')) )
\r
435 (*pShader)->DecRef();
\r
436 *pShader = QERApp_Shader_ForName(surfShaderName);
\r
442 // Model specified shader, if needed
\r
443 if( !(*pShader) || (stricmp(surfShaderName,(*pShader)->getName()) && !(surfShaderName[0] == '\0')) )
\r
446 (*pShader)->DecRef();
\r
447 *pShader = QERApp_Shader_ForName(surfShaderName);
\r