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