]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - plugins/model/remap.cpp
Increasing stack size on Windows build.
[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       // Determine which shader it is going to be
227       if( !shader ) {
228         if( global_shader ) {
229           shader = global_shader;
230         } else {
231           shader = QERApp_Shader_ForName(surfShaderName);
232         }
233       }
234       // Add reference
235       shader->IncRef();
236       // Done, continue
237       m_shaders.push_back( shader );
238     }
239   }
240   
241   inline IShader* shader_for_remap(const char* remap)
242   {
243     remap_t *pRemap;
244     remaps_t::iterator i;
245     for(i = m_remaps.begin(); i != m_remaps.end(); ++i)
246     {
247       pRemap = (*i);
248       if( stricmp( remap, pRemap->original ) == 0 )
249         break;
250     }
251     return (i != m_remaps.end()) ? QERApp_Shader_ForName(pRemap->remap) : NULL;
252   }
253
254   Str m_name;
255   int m_frame;
256   CPicoModel* m_model;
257
258   typedef vector<remap_t *> remaps_t;
259   remaps_t m_remaps;
260   typedef vector<IShader*> shaders_t;
261   shaders_t m_shaders;
262 };
263
264 class ModelWrapper : public IRender, public ISelect
265 {
266   unsigned int m_refcount;
267 public:
268   ModelWrapper(entity_interfaces_t* model, const char* name)
269     : m_refcount(1), m_name(name)
270   {
271     m_model = g_model_cache.capture(ModelCache::key_type(m_name.GetBuffer(), 0));
272
273     model->pRender = this;
274     model->pRender->IncRef();
275     model->pEdit = NULL;
276     model->pSelect = this;
277     model->pSelect->IncRef();
278   }
279   virtual ~ModelWrapper()
280   {
281     g_model_cache.release(ModelCache::key_type(m_name.GetBuffer(), 0));
282         }
283
284   virtual void IncRef()
285   {
286     ++m_refcount;
287   }
288   virtual void DecRef()
289   {
290     if(--m_refcount == 0)
291       delete this;
292   }
293   virtual void Draw(int state, int rflags) const
294   {
295     m_model->Draw(state, rflags);
296   }
297   virtual const aabb_t *GetAABB() const 
298   {
299     return m_model->GetAABB();
300   }
301   virtual bool TestRay(const ray_t *ray, vec_t *dist) const
302   {
303     return m_model->TestRay(ray, dist);
304   }
305
306   Str m_name;
307   CPicoModel* m_model;
308 };
309
310 void LoadModel(entity_interfaces_t* model, const char* name)
311 {
312   if(strchr(name, ':') != NULL || strchr(name, '?') != NULL || strchr(name, '&') != NULL)
313   {
314     RemapWrapper* wrapper = new RemapWrapper(model, name);
315     wrapper->DecRef();
316   }
317   else
318   {
319     ModelWrapper* wrapper = new ModelWrapper(model, name);
320     wrapper->DecRef();
321   }
322 }