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