transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / plugins / model / remap.cpp
1 \r
2 #include "cpicomodel.h"\r
3 #include "qertypes.h"\r
4 \r
5 #include <map>\r
6 #include <vector>\r
7 \r
8 #define RADIANT_ASSERT(condition, message) if(!(condition)) { Sys_Printf("ASSERTION FAILURE: " message "\n"); } else\r
9 \r
10 template<class key_type, class value_type>\r
11 class cache_element\r
12 {\r
13 public:\r
14   inline cache_element() : m_count(0), m_value(NULL) {}\r
15   inline ~cache_element()\r
16   {\r
17     RADIANT_ASSERT(m_count == 0 , "destroyed a reference before it was released\n");\r
18     if(m_count > 0)\r
19       destroy();\r
20   }\r
21   inline value_type* capture(const key_type& key)\r
22   {\r
23     if(++m_count == 1)\r
24       construct(key);\r
25     return m_value;\r
26   }\r
27   inline void release()\r
28   {\r
29     RADIANT_ASSERT(!empty(), "failed to release reference - not found in cache\n");\r
30     if(--m_count == 0)\r
31       destroy();\r
32   }\r
33   inline bool empty()\r
34   {\r
35     return m_count == 0;\r
36   }\r
37   inline void refresh(const key_type& key)\r
38   {\r
39     m_value->refresh(key);\r
40   }\r
41 private:\r
42   inline void construct(const key_type& key)\r
43   {\r
44     m_value = new value_type(key);\r
45   }\r
46   inline void destroy()\r
47   {\r
48     delete m_value;\r
49   }\r
50 \r
51   unsigned int m_count;\r
52   value_type* m_value;\r
53 };\r
54 \r
55 class ModelCache\r
56 {\r
57   typedef CPicoModel value_type;\r
58   \r
59 public:\r
60   typedef PicoModelKey key_type;\r
61   typedef cache_element<key_type, value_type> elem_type;\r
62   typedef map<key_type, elem_type> cache_type;\r
63   \r
64   value_type* capture(const key_type& key)\r
65   {\r
66     return m_cache[key].capture(key);\r
67   }\r
68   void release(const key_type& key)\r
69   {\r
70     m_cache[key].release();\r
71   }\r
72 \r
73 private:\r
74   cache_type m_cache;\r
75 };\r
76 \r
77 ModelCache g_model_cache;\r
78 \r
79 \r
80 \r
81 typedef struct remap_s {\r
82   char m_remapbuff[64+1024];\r
83   char *original;\r
84   char *remap;\r
85 } remap_t;\r
86 \r
87 class RemapWrapper : public IRender, public ISelect\r
88 {\r
89   unsigned int m_refcount;\r
90 public:\r
91   RemapWrapper(entity_interfaces_t* model, const char* name)\r
92     : m_refcount(1)\r
93   {\r
94     parse_namestr(name);\r
95 \r
96     m_model = g_model_cache.capture(ModelCache::key_type(m_name.GetBuffer(), m_frame));\r
97 \r
98     model->pRender = this;\r
99     model->pRender->IncRef();\r
100     model->pEdit = NULL;\r
101     model->pSelect = this;\r
102     model->pSelect->IncRef();\r
103 \r
104     construct_shaders();\r
105   }\r
106   virtual ~RemapWrapper()\r
107   {\r
108     g_model_cache.release(ModelCache::key_type(m_name.GetBuffer(), m_frame));\r
109 \r
110     for(shaders_t::iterator i = m_shaders.begin(); i != m_shaders.end(); ++i) {\r
111       (*i)->DecRef();\r
112         }\r
113 \r
114     for(remaps_t::iterator j = m_remaps.begin(); j != m_remaps.end(); ++j)\r
115     {\r
116       remap_t *pRemap = (*j);\r
117       delete pRemap;\r
118     }\r
119     m_remaps.clear();\r
120   }\r
121   virtual void IncRef()\r
122   {\r
123     ++m_refcount;\r
124   }\r
125   virtual void DecRef()\r
126   {\r
127     if(--m_refcount == 0)\r
128       delete this;\r
129   }\r
130   virtual void Draw(int state, int rflags) const\r
131   {\r
132     m_model->Draw(state, m_shaders, rflags);\r
133   }\r
134   virtual const aabb_t *GetAABB() const \r
135   {\r
136     return m_model->GetAABB();\r
137   }\r
138   virtual bool TestRay(const ray_t *ray, vec_t *dist) const\r
139   {\r
140     return m_model->TestRay(ray, dist);\r
141   }\r
142 private:\r
143   void add_remap(const char *remap)\r
144   {\r
145     const char *ch;\r
146     remap_t *pRemap;\r
147 \r
148     ch = remap;\r
149 \r
150     while( *ch && *ch != ';' )\r
151       ch++;\r
152 \r
153     if( *ch == '\0' ) {\r
154       // bad remap\r
155       Sys_FPrintf( SYS_WRN, "WARNING: Shader _remap key found in a model entity without a ; character\n" );\r
156     } else {\r
157       pRemap = new remap_t;\r
158 \r
159       strncpy( pRemap->m_remapbuff, remap, sizeof(pRemap->m_remapbuff) );\r
160 \r
161       pRemap->m_remapbuff[ch - remap] = '\0';\r
162 \r
163       pRemap->original = pRemap->m_remapbuff;\r
164       pRemap->remap = pRemap->m_remapbuff + ( ch - remap ) + 1;\r
165 \r
166       m_remaps.push_back( pRemap );\r
167     }\r
168   }\r
169 \r
170   void parse_namestr(const char *name)\r
171   {\r
172     const char *ptr, *s;\r
173     char buf[1024];\r
174     bool hasName, hasFrame;\r
175 \r
176     hasName = hasFrame = false;\r
177 \r
178     for( s = ptr = name; *ptr; ptr++ ) {\r
179       if( !hasName && *ptr == ':' ) {\r
180         // model name\r
181         hasName = true;\r
182         strncpy( buf, s, ptr - s );\r
183         buf[ptr - s] = '\0';\r
184         m_name = buf;\r
185         s = ptr + 1;\r
186       } else if( *ptr == '?' ) {\r
187         // model frame\r
188         hasFrame = true;\r
189         strncpy( buf, s, ptr - s );\r
190         buf[ptr - s] = '\0';\r
191         m_frame = atoi(buf);\r
192         s = ptr + 1;\r
193       } else if( *ptr == '&' ) {\r
194         // a remap\r
195         strncpy( buf, s, ptr - s );\r
196         buf[ptr - s] = '\0';\r
197         add_remap( buf );\r
198         s = ptr + 1;\r
199       }\r
200     }\r
201 \r
202     if( !hasFrame ) {\r
203       // model frame\r
204       strncpy( buf, s, ptr - s );\r
205       buf[ptr - s] = '\0';\r
206       m_frame = atoi(buf);\r
207     } else {\r
208       // a remap\r
209       strncpy( buf, s, ptr - s );\r
210       buf[ptr - s] = '\0';\r
211       add_remap( buf );\r
212     }\r
213   }\r
214 \r
215   void construct_shaders()\r
216   {\r
217     IShader* global_shader = shader_for_remap("*");\r
218 \r
219     unsigned int numSurfaces = m_model->GetNumSurfaces();\r
220     m_shaders.reserve(numSurfaces);\r
221     // now go through our surface and find our shaders, remap if needed\r
222     for(unsigned int j = 0; j < numSurfaces; j++ )\r
223     {\r
224       const char* surfShaderName = m_model->GetShaderNameForSurface(j);\r
225       IShader* shader = shader_for_remap(surfShaderName);\r
226 //      m_shaders.push_back((shader) ? shader : (global_shader) ? global_shader : QERApp_Shader_ForName(surfShaderName));\r
227       if( shader ) {\r
228         m_shaders.push_back(shader);\r
229       } else if( global_shader ) {\r
230         m_shaders.push_back(global_shader);\r
231       } else {\r
232         m_shaders.push_back(QERApp_Shader_ForName(surfShaderName));\r
233       }\r
234     }\r
235   }\r
236   \r
237   inline IShader* shader_for_remap(const char* remap)\r
238   {\r
239     remap_t *pRemap;\r
240     remaps_t::iterator i;\r
241     for(i = m_remaps.begin(); i != m_remaps.end(); ++i)\r
242     {\r
243       pRemap = (*i);\r
244       if( stricmp( remap, pRemap->original ) == 0 )\r
245         break;\r
246     }\r
247     return (i != m_remaps.end()) ? QERApp_Shader_ForName(pRemap->remap) : NULL;\r
248   }\r
249 \r
250   Str m_name;\r
251   int m_frame;\r
252   CPicoModel* m_model;\r
253 \r
254   typedef vector<remap_t *> remaps_t;\r
255   remaps_t m_remaps;\r
256   typedef vector<IShader*> shaders_t;\r
257   shaders_t m_shaders;\r
258 };\r
259 \r
260 class ModelWrapper : public IRender, public ISelect\r
261 {\r
262   unsigned int m_refcount;\r
263 public:\r
264   ModelWrapper(entity_interfaces_t* model, const char* name)\r
265     : m_refcount(1), m_name(name)\r
266   {\r
267     m_model = g_model_cache.capture(ModelCache::key_type(m_name.GetBuffer(), 0));\r
268 \r
269     model->pRender = this;\r
270     model->pRender->IncRef();\r
271     model->pEdit = NULL;\r
272     model->pSelect = this;\r
273     model->pSelect->IncRef();\r
274   }\r
275   virtual ~ModelWrapper()\r
276   {\r
277     g_model_cache.release(ModelCache::key_type(m_name.GetBuffer(), 0));\r
278         }\r
279 \r
280   virtual void IncRef()\r
281   {\r
282     ++m_refcount;\r
283   }\r
284   virtual void DecRef()\r
285   {\r
286     if(--m_refcount == 0)\r
287       delete this;\r
288   }\r
289   virtual void Draw(int state, int rflags) const\r
290   {\r
291     m_model->Draw(state, rflags);\r
292   }\r
293   virtual const aabb_t *GetAABB() const \r
294   {\r
295     return m_model->GetAABB();\r
296   }\r
297   virtual bool TestRay(const ray_t *ray, vec_t *dist) const\r
298   {\r
299     return m_model->TestRay(ray, dist);\r
300   }\r
301 \r
302   Str m_name;\r
303   CPicoModel* m_model;\r
304 };\r
305 \r
306 void LoadModel(entity_interfaces_t* model, const char* name)\r
307 {\r
308   if(strchr(name, ':') != NULL || strchr(name, '?') != NULL || strchr(name, '&') != NULL)\r
309   {\r
310     RemapWrapper* wrapper = new RemapWrapper(model, name);\r
311     wrapper->DecRef();\r
312   }\r
313   else\r
314   {\r
315     ModelWrapper* wrapper = new ModelWrapper(model, name);\r
316     wrapper->DecRef();\r
317   }\r
318 }\r