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