]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - plugins/model/model.cpp
my own uncrustify run
[xonotic/netradiant.git] / plugins / model / model.cpp
1 /*
2    Copyright (C) 2001-2006, William Joseph.
3    All Rights Reserved.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #include "model.h"
23
24 #include "picomodel.h"
25
26 #include "iarchive.h"
27 #include "idatastream.h"
28 #include "imodel.h"
29 #include "modelskin.h"
30
31 #include "cullable.h"
32 #include "renderable.h"
33 #include "selectable.h"
34
35 #include "math/frustum.h"
36 #include "string/string.h"
37 #include "generic/static.h"
38 #include "shaderlib.h"
39 #include "scenelib.h"
40 #include "instancelib.h"
41 #include "transformlib.h"
42 #include "traverselib.h"
43 #include "render.h"
44
45 class VectorLightList : public LightList
46 {
47 typedef std::vector<const RendererLight*> Lights;
48 Lights m_lights;
49 public:
50 void addLight( const RendererLight& light ){
51         m_lights.push_back( &light );
52 }
53 void clear(){
54         m_lights.clear();
55 }
56 void evaluateLights() const {
57 }
58 void lightsChanged() const {
59 }
60 void forEachLight( const RendererLightCallback& callback ) const {
61         for ( Lights::const_iterator i = m_lights.begin(); i != m_lights.end(); ++i )
62         {
63                 callback( *( *i ) );
64         }
65 }
66 };
67
68 class PicoSurface :
69         public OpenGLRenderable
70 {
71 AABB m_aabb_local;
72 CopiedString m_shader;
73 Shader* m_state;
74
75 Array<ArbitraryMeshVertex> m_vertices;
76 Array<RenderIndex> m_indices;
77
78 public:
79
80 PicoSurface(){
81         constructNull();
82         CaptureShader();
83 }
84 PicoSurface( picoSurface_t* surface ){
85         CopyPicoSurface( surface );
86         CaptureShader();
87 }
88 ~PicoSurface(){
89         ReleaseShader();
90 }
91
92 void render( RenderStateFlags state ) const {
93         if ( ( state & RENDER_BUMP ) != 0 ) {
94                 if ( GlobalShaderCache().useShaderLanguage() ) {
95                         glNormalPointer( GL_FLOAT, sizeof( ArbitraryMeshVertex ), &m_vertices.data()->normal );
96                         glVertexAttribPointerARB( c_attr_TexCoord0, 2, GL_FLOAT, 0, sizeof( ArbitraryMeshVertex ), &m_vertices.data()->texcoord );
97                         glVertexAttribPointerARB( c_attr_Tangent, 3, GL_FLOAT, 0, sizeof( ArbitraryMeshVertex ), &m_vertices.data()->tangent );
98                         glVertexAttribPointerARB( c_attr_Binormal, 3, GL_FLOAT, 0, sizeof( ArbitraryMeshVertex ), &m_vertices.data()->bitangent );
99                 }
100                 else
101                 {
102                         glVertexAttribPointerARB( 11, 3, GL_FLOAT, 0, sizeof( ArbitraryMeshVertex ), &m_vertices.data()->normal );
103                         glVertexAttribPointerARB( 8, 2, GL_FLOAT, 0, sizeof( ArbitraryMeshVertex ), &m_vertices.data()->texcoord );
104                         glVertexAttribPointerARB( 9, 3, GL_FLOAT, 0, sizeof( ArbitraryMeshVertex ), &m_vertices.data()->tangent );
105                         glVertexAttribPointerARB( 10, 3, GL_FLOAT, 0, sizeof( ArbitraryMeshVertex ), &m_vertices.data()->bitangent );
106                 }
107         }
108         else
109         {
110                 glNormalPointer( GL_FLOAT, sizeof( ArbitraryMeshVertex ), &m_vertices.data()->normal );
111                 glTexCoordPointer( 2, GL_FLOAT, sizeof( ArbitraryMeshVertex ), &m_vertices.data()->texcoord );
112         }
113         glVertexPointer( 3, GL_FLOAT, sizeof( ArbitraryMeshVertex ), &m_vertices.data()->vertex );
114         glDrawElements( GL_TRIANGLES, GLsizei( m_indices.size() ), RenderIndexTypeID, m_indices.data() );
115
116 #if defined( _DEBUG )
117         GLfloat modelview[16];
118         glGetFloatv( GL_MODELVIEW_MATRIX, modelview ); // I know this is slow as hell, but hey - we're in _DEBUG
119         Matrix4 modelview_inv(
120                 modelview[0], modelview[1], modelview[2], modelview[3],
121                 modelview[4], modelview[5], modelview[6], modelview[7],
122                 modelview[8], modelview[9], modelview[10], modelview[11],
123                 modelview[12], modelview[13], modelview[14], modelview[15] );
124         matrix4_full_invert( modelview_inv );
125         Matrix4 modelview_inv_transposed = matrix4_transposed( modelview_inv );
126
127         glBegin( GL_LINES );
128
129         for ( Array<ArbitraryMeshVertex>::const_iterator i = m_vertices.begin(); i != m_vertices.end(); ++i )
130         {
131                 Vector3 normal = normal3f_to_vector3( ( *i ).normal );
132                 normal = matrix4_transformed_direction( modelview_inv, vector3_normalised( matrix4_transformed_direction( modelview_inv_transposed, normal ) ) ); // do some magic
133                 Vector3 normalTransformed = vector3_added( vertex3f_to_vector3( ( *i ).vertex ), vector3_scaled( normal, 8 ) );
134                 glVertex3fv( vertex3f_to_array( ( *i ).vertex ) );
135                 glVertex3fv( vector3_to_array( normalTransformed ) );
136         }
137         glEnd();
138 #endif
139 }
140
141 VolumeIntersectionValue intersectVolume( const VolumeTest& test, const Matrix4& localToWorld ) const {
142         return test.TestAABB( m_aabb_local, localToWorld );
143 }
144
145 const AABB& localAABB() const {
146         return m_aabb_local;
147 }
148
149 void render( Renderer& renderer, const Matrix4& localToWorld, Shader* state ) const {
150         renderer.SetState( state, Renderer::eFullMaterials );
151         renderer.addRenderable( *this, localToWorld );
152 }
153
154 void render( Renderer& renderer, const Matrix4& localToWorld ) const {
155         render( renderer, localToWorld, m_state );
156 }
157
158 void testSelect( Selector& selector, SelectionTest& test, const Matrix4& localToWorld ){
159         test.BeginMesh( localToWorld );
160
161         SelectionIntersection best;
162         testSelect( test, best );
163         if ( best.valid() ) {
164                 selector.addIntersection( best );
165         }
166 }
167
168 const char* getShader() const {
169         return m_shader.c_str();
170 }
171 Shader* getState() const {
172         return m_state;
173 }
174
175 private:
176
177 void CaptureShader(){
178         m_state = GlobalShaderCache().capture( m_shader.c_str() );
179 }
180 void ReleaseShader(){
181         GlobalShaderCache().release( m_shader.c_str() );
182 }
183
184 void UpdateAABB(){
185         m_aabb_local = AABB();
186         for ( std::size_t i = 0; i < m_vertices.size(); ++i )
187                 aabb_extend_by_point_safe( m_aabb_local, reinterpret_cast<const Vector3&>( m_vertices[i].vertex ) );
188
189
190         for ( Array<RenderIndex>::iterator i = m_indices.begin(); i != m_indices.end(); i += 3 )
191         {
192                 ArbitraryMeshVertex& a = m_vertices[*( i + 0 )];
193                 ArbitraryMeshVertex& b = m_vertices[*( i + 1 )];
194                 ArbitraryMeshVertex& c = m_vertices[*( i + 2 )];
195
196                 ArbitraryMeshTriangle_sumTangents( a, b, c );
197         }
198
199         for ( Array<ArbitraryMeshVertex>::iterator i = m_vertices.begin(); i != m_vertices.end(); ++i )
200         {
201                 vector3_normalise( reinterpret_cast<Vector3&>( ( *i ).tangent ) );
202                 vector3_normalise( reinterpret_cast<Vector3&>( ( *i ).bitangent ) );
203         }
204 }
205
206 void testSelect( SelectionTest& test, SelectionIntersection& best ){
207         test.TestTriangles(
208                 VertexPointer( VertexPointer::pointer( &m_vertices.data()->vertex ), sizeof( ArbitraryMeshVertex ) ),
209                 IndexPointer( m_indices.data(), IndexPointer::index_type( m_indices.size() ) ),
210                 best
211                 );
212 }
213
214 void CopyPicoSurface( picoSurface_t* surface ){
215         picoShader_t* shader = PicoGetSurfaceShader( surface );
216         if ( shader == 0 ) {
217                 m_shader = "";
218         }
219         else{
220                 m_shader = PicoGetShaderName( shader );
221         }
222
223         m_vertices.resize( PicoGetSurfaceNumVertexes( surface ) );
224         m_indices.resize( PicoGetSurfaceNumIndexes( surface ) );
225
226         for ( std::size_t i = 0; i < m_vertices.size(); ++i )
227         {
228                 picoVec_t* xyz = PicoGetSurfaceXYZ( surface, int(i) );
229                 m_vertices[i].vertex = vertex3f_from_array( xyz );
230
231                 picoVec_t* normal = PicoGetSurfaceNormal( surface, int(i) );
232                 m_vertices[i].normal = normal3f_from_array( normal );
233
234                 picoVec_t* st = PicoGetSurfaceST( surface, 0, int(i) );
235                 m_vertices[i].texcoord = TexCoord2f( st[0], st[1] );
236
237 #if 0
238                 picoVec_t* color = PicoGetSurfaceColor( surface, 0, int(i) );
239                 m_vertices[i].colour = Colour4b( color[0], color[1], color[2], color[3] );
240 #endif
241         }
242
243         picoIndex_t* indexes = PicoGetSurfaceIndexes( surface, 0 );
244         for ( std::size_t j = 0; j < m_indices.size(); ++j )
245                 m_indices[ j ] = indexes[ j ];
246
247         UpdateAABB();
248 }
249
250 void constructQuad( std::size_t index, const Vector3& a, const Vector3& b, const Vector3& c, const Vector3& d, const Vector3& normal ){
251         m_vertices[index * 4 + 0] = ArbitraryMeshVertex(
252                 vertex3f_for_vector3( a ),
253                 normal3f_for_vector3( normal ),
254                 texcoord2f_from_array( aabb_texcoord_topleft )
255                 );
256         m_vertices[index * 4 + 1] = ArbitraryMeshVertex(
257                 vertex3f_for_vector3( b ),
258                 normal3f_for_vector3( normal ),
259                 texcoord2f_from_array( aabb_texcoord_topright )
260                 );
261         m_vertices[index * 4 + 2] = ArbitraryMeshVertex(
262                 vertex3f_for_vector3( c ),
263                 normal3f_for_vector3( normal ),
264                 texcoord2f_from_array( aabb_texcoord_botright )
265                 );
266         m_vertices[index * 4 + 3] = ArbitraryMeshVertex(
267                 vertex3f_for_vector3( d ),
268                 normal3f_for_vector3( normal ),
269                 texcoord2f_from_array( aabb_texcoord_botleft )
270                 );
271 }
272
273 void constructNull(){
274         AABB aabb( Vector3( 0, 0, 0 ), Vector3( 8, 8, 8 ) );
275
276         Vector3 points[8];
277         aabb_corners( aabb, points );
278
279         m_vertices.resize( 24 );
280
281         constructQuad( 0, points[2], points[1], points[5], points[6], aabb_normals[0] );
282         constructQuad( 1, points[1], points[0], points[4], points[5], aabb_normals[1] );
283         constructQuad( 2, points[0], points[1], points[2], points[3], aabb_normals[2] );
284         constructQuad( 3, points[0], points[3], points[7], points[4], aabb_normals[3] );
285         constructQuad( 4, points[3], points[2], points[6], points[7], aabb_normals[4] );
286         constructQuad( 5, points[7], points[6], points[5], points[4], aabb_normals[5] );
287
288         m_indices.resize( 36 );
289
290         RenderIndex indices[36] = {
291                 0,  1,  2,  0,  2,  3,
292                 4,  5,  6,  4,  6,  7,
293                 8,  9, 10,  8, 10, 11,
294                 12, 13, 14, 12, 14, 15,
295                 16, 17, 18, 16, 18, 19,
296                 20, 21, 22, 10, 22, 23,
297         };
298
299
300         Array<RenderIndex>::iterator j = m_indices.begin();
301         for ( RenderIndex* i = indices; i != indices + ( sizeof( indices ) / sizeof( RenderIndex ) ); ++i )
302         {
303                 *j++ = *i;
304         }
305
306         m_shader = "";
307
308         UpdateAABB();
309 }
310 };
311
312
313 typedef std::pair<CopiedString, int> PicoModelKey;
314
315
316 class PicoModel :
317         public Cullable,
318         public Bounded
319 {
320 typedef std::vector<PicoSurface*> surfaces_t;
321 surfaces_t m_surfaces;
322
323 AABB m_aabb_local;
324 public:
325 Callback m_lightsChanged;
326
327 PicoModel(){
328         constructNull();
329 }
330 PicoModel( picoModel_t* model ){
331         CopyPicoModel( model );
332 }
333 ~PicoModel(){
334         for ( surfaces_t::iterator i = m_surfaces.begin(); i != m_surfaces.end(); ++i )
335                 delete *i;
336 }
337
338 typedef surfaces_t::const_iterator const_iterator;
339
340 const_iterator begin() const {
341         return m_surfaces.begin();
342 }
343 const_iterator end() const {
344         return m_surfaces.end();
345 }
346 std::size_t size() const {
347         return m_surfaces.size();
348 }
349
350 VolumeIntersectionValue intersectVolume( const VolumeTest& test, const Matrix4& localToWorld ) const {
351         return test.TestAABB( m_aabb_local, localToWorld );
352 }
353
354 virtual const AABB& localAABB() const {
355         return m_aabb_local;
356 }
357
358 void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld, std::vector<Shader*> states ) const {
359         for ( surfaces_t::const_iterator i = m_surfaces.begin(); i != m_surfaces.end(); ++i )
360         {
361                 if ( ( *i )->intersectVolume( volume, localToWorld ) != c_volumeOutside ) {
362                         ( *i )->render( renderer, localToWorld, states[i - m_surfaces.begin()] );
363                 }
364         }
365 }
366
367 void testSelect( Selector& selector, SelectionTest& test, const Matrix4& localToWorld ){
368         for ( surfaces_t::iterator i = m_surfaces.begin(); i != m_surfaces.end(); ++i )
369         {
370                 if ( ( *i )->intersectVolume( test.getVolume(), localToWorld ) != c_volumeOutside ) {
371                         ( *i )->testSelect( selector, test, localToWorld );
372                 }
373         }
374 }
375
376 private:
377 void CopyPicoModel( picoModel_t* model ){
378         m_aabb_local = AABB();
379
380         /* each surface on the model will become a new map drawsurface */
381         int numSurfaces = PicoGetModelNumSurfaces( model );
382         //%  SYs_FPrintf( SYS_VRB, "Model %s has %d surfaces\n", name, numSurfaces );
383         for ( int s = 0; s < numSurfaces; ++s )
384         {
385                 /* get surface */
386                 picoSurface_t* surface = PicoGetModelSurface( model, s );
387                 if ( surface == 0 ) {
388                         continue;
389                 }
390
391                 /* only handle triangle surfaces initially (fixme: support patches) */
392                 if ( PicoGetSurfaceType( surface ) != PICO_TRIANGLES ) {
393                         continue;
394                 }
395
396                 /* fix the surface's normals */
397                 PicoFixSurfaceNormals( surface );
398
399                 PicoSurface* picosurface = new PicoSurface( surface );
400                 aabb_extend_by_aabb_safe( m_aabb_local, picosurface->localAABB() );
401                 m_surfaces.push_back( picosurface );
402         }
403 }
404 void constructNull(){
405         PicoSurface* picosurface = new PicoSurface();
406         m_aabb_local = picosurface->localAABB();
407         m_surfaces.push_back( picosurface );
408 }
409 };
410
411 inline void Surface_addLight( PicoSurface& surface, VectorLightList& lights, const Matrix4& localToWorld, const RendererLight& light ){
412         if ( light.testAABB( aabb_for_oriented_aabb( surface.localAABB(), localToWorld ) ) ) {
413                 lights.addLight( light );
414         }
415 }
416
417 class PicoModelInstance :
418         public scene::Instance,
419         public Renderable,
420         public SelectionTestable,
421         public LightCullable,
422         public SkinnedModel
423 {
424 class TypeCasts
425 {
426 InstanceTypeCastTable m_casts;
427 public:
428 TypeCasts(){
429         InstanceContainedCast<PicoModelInstance, Bounded>::install( m_casts );
430         InstanceContainedCast<PicoModelInstance, Cullable>::install( m_casts );
431         InstanceStaticCast<PicoModelInstance, Renderable>::install( m_casts );
432         InstanceStaticCast<PicoModelInstance, SelectionTestable>::install( m_casts );
433         InstanceStaticCast<PicoModelInstance, SkinnedModel>::install( m_casts );
434 }
435 InstanceTypeCastTable& get(){
436         return m_casts;
437 }
438 };
439
440 PicoModel& m_picomodel;
441
442 const LightList* m_lightList;
443 typedef Array<VectorLightList> SurfaceLightLists;
444 SurfaceLightLists m_surfaceLightLists;
445
446 class Remap
447 {
448 public:
449 CopiedString first;
450 Shader* second;
451 Remap() : second( 0 ){
452 }
453 };
454 typedef Array<Remap> SurfaceRemaps;
455 SurfaceRemaps m_skins;
456
457 PicoModelInstance( const PicoModelInstance& );
458 PicoModelInstance operator=( const PicoModelInstance& );
459 public:
460 typedef LazyStatic<TypeCasts> StaticTypeCasts;
461
462 void* m_test;
463
464 Bounded& get( NullType<Bounded>){
465         return m_picomodel;
466 }
467 Cullable& get( NullType<Cullable>){
468         return m_picomodel;
469 }
470
471 void lightsChanged(){
472         m_lightList->lightsChanged();
473 }
474 typedef MemberCaller<PicoModelInstance, &PicoModelInstance::lightsChanged> LightsChangedCaller;
475
476 void constructRemaps(){
477         ASSERT_MESSAGE( m_skins.size() == m_picomodel.size(), "ERROR" );
478         ModelSkin* skin = NodeTypeCast<ModelSkin>::cast( path().parent() );
479         if ( skin != 0 && skin->realised() ) {
480                 SurfaceRemaps::iterator j = m_skins.begin();
481                 for ( PicoModel::const_iterator i = m_picomodel.begin(); i != m_picomodel.end(); ++i, ++j )
482                 {
483                         const char* remap = skin->getRemap( ( *i )->getShader() );
484                         if ( !string_empty( remap ) ) {
485                                 ( *j ).first = remap;
486                                 ( *j ).second = GlobalShaderCache().capture( remap );
487                         }
488                         else
489                         {
490                                 ( *j ).second = 0;
491                         }
492                 }
493                 SceneChangeNotify();
494         }
495 }
496 void destroyRemaps(){
497         ASSERT_MESSAGE( m_skins.size() == m_picomodel.size(), "ERROR" );
498         for ( SurfaceRemaps::iterator i = m_skins.begin(); i != m_skins.end(); ++i )
499         {
500                 if ( ( *i ).second != 0 ) {
501                         GlobalShaderCache().release( ( *i ).first.c_str() );
502                         ( *i ).second = 0;
503                 }
504         }
505 }
506 void skinChanged(){
507         destroyRemaps();
508         constructRemaps();
509 }
510
511 PicoModelInstance( const scene::Path& path, scene::Instance* parent, PicoModel& picomodel ) :
512         Instance( path, parent, this, StaticTypeCasts::instance().get() ),
513         m_picomodel( picomodel ),
514         m_surfaceLightLists( m_picomodel.size() ),
515         m_skins( m_picomodel.size() ){
516         m_lightList = &GlobalShaderCache().attach( *this );
517         m_picomodel.m_lightsChanged = LightsChangedCaller( *this );
518
519         Instance::setTransformChangedCallback( LightsChangedCaller( *this ) );
520
521         constructRemaps();
522 }
523 ~PicoModelInstance(){
524         destroyRemaps();
525
526         Instance::setTransformChangedCallback( Callback() );
527
528         m_picomodel.m_lightsChanged = Callback();
529         GlobalShaderCache().detach( *this );
530 }
531
532 void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld ) const {
533         SurfaceLightLists::const_iterator j = m_surfaceLightLists.begin();
534         SurfaceRemaps::const_iterator k = m_skins.begin();
535         for ( PicoModel::const_iterator i = m_picomodel.begin(); i != m_picomodel.end(); ++i, ++j, ++k )
536         {
537                 if ( ( *i )->intersectVolume( volume, localToWorld ) != c_volumeOutside ) {
538                         renderer.setLights( *j );
539                         ( *i )->render( renderer, localToWorld, ( *k ).second != 0 ? ( *k ).second : ( *i )->getState() );
540                 }
541         }
542 }
543
544 void renderSolid( Renderer& renderer, const VolumeTest& volume ) const {
545         m_lightList->evaluateLights();
546
547         render( renderer, volume, Instance::localToWorld() );
548 }
549 void renderWireframe( Renderer& renderer, const VolumeTest& volume ) const {
550         renderSolid( renderer, volume );
551 }
552
553 void testSelect( Selector& selector, SelectionTest& test ){
554         m_picomodel.testSelect( selector, test, Instance::localToWorld() );
555 }
556
557 bool testLight( const RendererLight& light ) const {
558         return light.testAABB( worldAABB() );
559 }
560 void insertLight( const RendererLight& light ){
561         const Matrix4& localToWorld = Instance::localToWorld();
562         SurfaceLightLists::iterator j = m_surfaceLightLists.begin();
563         for ( PicoModel::const_iterator i = m_picomodel.begin(); i != m_picomodel.end(); ++i )
564         {
565                 Surface_addLight( *( *i ), *j++, localToWorld, light );
566         }
567 }
568 void clearLights(){
569         for ( SurfaceLightLists::iterator i = m_surfaceLightLists.begin(); i != m_surfaceLightLists.end(); ++i )
570         {
571                 ( *i ).clear();
572         }
573 }
574 };
575
576 class PicoModelNode : public scene::Node::Symbiot, public scene::Instantiable
577 {
578 class TypeCasts
579 {
580 NodeTypeCastTable m_casts;
581 public:
582 TypeCasts(){
583         NodeStaticCast<PicoModelNode, scene::Instantiable>::install( m_casts );
584 }
585 NodeTypeCastTable& get(){
586         return m_casts;
587 }
588 };
589
590
591 scene::Node m_node;
592 InstanceSet m_instances;
593 PicoModel m_picomodel;
594
595 public:
596 typedef LazyStatic<TypeCasts> StaticTypeCasts;
597
598 PicoModelNode() : m_node( this, this, StaticTypeCasts::instance().get() ){
599 }
600 PicoModelNode( picoModel_t* model ) : m_node( this, this, StaticTypeCasts::instance().get() ), m_picomodel( model ){
601 }
602
603 void release(){
604         delete this;
605 }
606 scene::Node& node(){
607         return m_node;
608 }
609
610 scene::Instance* create( const scene::Path& path, scene::Instance* parent ){
611         return new PicoModelInstance( path, parent, m_picomodel );
612 }
613 void forEachInstance( const scene::Instantiable::Visitor& visitor ){
614         m_instances.forEachInstance( visitor );
615 }
616 void insert( scene::Instantiable::Observer* observer, const scene::Path& path, scene::Instance* instance ){
617         m_instances.insert( observer, path, instance );
618 }
619 scene::Instance* erase( scene::Instantiable::Observer* observer, const scene::Path& path ){
620         return m_instances.erase( observer, path );
621 }
622 };
623
624
625 #if 0
626
627 template<typename Key, typename Type>
628 class create_new
629 {
630 public:
631 static Type* construct( const Key& key ){
632         return new Type( key );
633 }
634 static void destroy( Type* value ){
635         delete value;
636 }
637 };
638
639 template<typename Key, typename Type, typename creation_policy = create_new<Key, Type> >
640 class cache_element : public creation_policy
641 {
642 public:
643 inline cache_element() : m_count( 0 ), m_value( 0 ) {}
644 inline ~cache_element(){
645         ASSERT_MESSAGE( m_count == 0, "destroyed a reference before it was released\n" );
646         if ( m_count > 0 ) {
647                 destroy();
648         }
649 }
650 inline Type* capture( const Key& key ){
651         if ( ++m_count == 1 ) {
652                 construct( key );
653         }
654         return m_value;
655 }
656 inline void release(){
657         ASSERT_MESSAGE( !empty(), "failed to release reference - not found in cache\n" );
658         if ( --m_count == 0 ) {
659                 destroy();
660         }
661 }
662 inline bool empty(){
663         return m_count == 0;
664 }
665 inline void refresh( const Key& key ){
666         m_value->refresh( key );
667 }
668 private:
669 inline void construct( const Key& key ){
670         m_value = creation_policy::construct( key );
671 }
672 inline void destroy(){
673         creation_policy::destroy( m_value );
674 }
675
676 std::size_t m_count;
677 Type* m_value;
678 };
679
680 class create_picomodel
681 {
682 typedef PicoModelKey key_type;
683 typedef PicoModel value_type;
684 public:
685 static value_type* construct( const key_type& key ){
686         picoModel_t* picomodel = PicoLoadModel( const_cast<char*>( key.first.c_str() ), key.second );
687         value_type* value = new value_type( picomodel );
688         PicoFreeModel( picomodel );
689         return value;
690 }
691 static void destroy( value_type* value ){
692         delete value;
693 }
694 };
695
696 #include <map>
697
698 class ModelCache
699 {
700 typedef PicoModel value_type;
701
702 public:
703 typedef PicoModelKey key_type;
704 typedef cache_element<key_type, value_type, create_picomodel> elem_type;
705 typedef std::map<key_type, elem_type> cache_type;
706
707 value_type* capture( const key_type& key ){
708         return m_cache[key].capture( key );
709 }
710 void release( const key_type& key ){
711         m_cache[key].release();
712 }
713
714 private:
715 cache_type m_cache;
716 };
717
718 ModelCache g_model_cache;
719
720
721
722 typedef struct remap_s {
723         char m_remapbuff[64 + 1024];
724         char *original;
725         char *remap;
726 } remap_t;
727
728 class RemapWrapper :
729         public Cullable,
730         public Bounded
731 {
732 public:
733 RemapWrapper( const char* name ){
734         parse_namestr( name );
735
736         m_model = g_model_cache.capture( ModelCache::key_type( m_name, m_frame ) );
737
738         construct_shaders();
739 }
740 virtual ~RemapWrapper(){
741         g_model_cache.release( ModelCache::key_type( m_name, m_frame ) );
742
743         for ( shaders_t::iterator i = m_shaders.begin(); i != m_shaders.end(); ++i )
744         {
745                 GlobalShaderCache().release( ( *i ).c_str() );
746         }
747
748         for ( remaps_t::iterator j = m_remaps.begin(); j != m_remaps.end(); ++j )
749         {
750                 delete ( *j );
751         }
752 }
753
754 VolumeIntersectionValue intersectVolume( const VolumeTest& test, const Matrix4& localToWorld ) const {
755         return m_model->intersectVolume( test, localToWorld );
756 }
757
758 virtual const AABB& localAABB() const {
759         return m_model->localAABB();
760 }
761
762 void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld ) const {
763         m_model->render( renderer, volume, localToWorld, m_states );
764 }
765
766 void testSelect( Selector& selector, SelectionTest& test, const Matrix4& localToWorld ){
767         m_model->testSelect( selector, test, localToWorld );
768 }
769
770 private:
771 void add_remap( const char *remap ){
772         const char *ch;
773         remap_t *pRemap;
774
775         ch = remap;
776
777         while ( *ch && *ch != ';' )
778                 ch++;
779
780         if ( *ch == '\0' ) {
781                 // bad remap
782                 globalErrorStream() << "WARNING: Shader _remap key found in a model entity without a ; character\n";
783         }
784         else {
785                 pRemap = new remap_t;
786
787                 strncpy( pRemap->m_remapbuff, remap, sizeof( pRemap->m_remapbuff ) );
788
789                 pRemap->m_remapbuff[ch - remap] = '\0';
790
791                 pRemap->original = pRemap->m_remapbuff;
792                 pRemap->remap = pRemap->m_remapbuff + ( ch - remap ) + 1;
793
794                 m_remaps.push_back( pRemap );
795         }
796 }
797
798 void parse_namestr( const char *name ){
799         const char *ptr, *s;
800         bool hasName, hasFrame;
801
802         hasName = hasFrame = false;
803
804         m_frame = 0;
805
806         for ( s = ptr = name; ; ++ptr )
807         {
808                 if ( !hasName && ( *ptr == ':' || *ptr == '\0' ) ) {
809                         // model name
810                         hasName = true;
811                         m_name = CopiedString( s, ptr );
812                         s = ptr + 1;
813                 }
814                 else if ( *ptr == '?' || *ptr == '\0' ) {
815                         // model frame
816                         hasFrame = true;
817                         m_frame = atoi( CopiedString( s, ptr ).c_str() );
818                         s = ptr + 1;
819                 }
820                 else if ( *ptr == '&' || *ptr == '\0' ) {
821                         // a remap
822                         add_remap( CopiedString( s, ptr ).c_str() );
823                         s = ptr + 1;
824                 }
825
826                 if ( *ptr == '\0' ) {
827                         break;
828                 }
829         }
830 }
831
832 void construct_shaders(){
833         const char* global_shader = shader_for_remap( "*" );
834
835         m_shaders.reserve( m_model->size() );
836         m_states.reserve( m_model->size() );
837         for ( PicoModel::iterator i = m_model->begin(); i != m_model->end(); ++i )
838         {
839                 const char* shader = shader_for_remap( ( *i )->getShader() );
840                 m_shaders.push_back(
841                         ( shader[0] != '\0' )
842                         ? shader
843                         : ( global_shader[0] != '\0' )
844                         ? global_shader
845                         : ( *i )->getShader() );
846                 m_states.push_back( GlobalShaderCache().capture( m_shaders.back().c_str() ) );
847         }
848 }
849
850 inline const char* shader_for_remap( const char* remap ){
851         for ( remaps_t::iterator i = m_remaps.begin(); i != m_remaps.end(); ++i )
852         {
853                 if ( shader_equal( remap, ( *i )->original ) ) {
854                         return ( *i )->remap;
855                 }
856         }
857         return "";
858 }
859
860 CopiedString m_name;
861 int m_frame;
862 PicoModel* m_model;
863
864 typedef std::vector<remap_t*> remaps_t;
865 remaps_t m_remaps;
866 typedef std::vector<CopiedString> shaders_t;
867 shaders_t m_shaders;
868 typedef std::vector<Shader*> states_t;
869 states_t m_states;
870 };
871
872 class RemapWrapperInstance : public scene::Instance, public Renderable, public SelectionTestable
873 {
874 RemapWrapper& m_remapwrapper;
875 public:
876 RemapWrapperInstance( const scene::Path& path, scene::Instance* parent, RemapWrapper& remapwrapper ) : Instance( path, parent ), m_remapwrapper( remapwrapper ){
877         scene::Instance::m_cullable = &m_remapwrapper;
878         scene::Instance::m_render = this;
879         scene::Instance::m_select = this;
880 }
881
882 void renderSolid( Renderer& renderer, const VolumeTest& volume ) const {
883         m_remapwrapper.render( renderer, volume, Instance::localToWorld() );
884 }
885 void renderWireframe( Renderer& renderer, const VolumeTest& volume ) const {
886         renderSolid( renderer, volume );
887 }
888
889 void testSelect( Selector& selector, SelectionTest& test ){
890         m_remapwrapper.testSelect( selector, test, Instance::localToWorld() );
891 }
892 };
893
894 class RemapWrapperNode : public scene::Node::Symbiot, public scene::Instantiable
895 {
896 scene::Node m_node;
897 typedef RemapWrapperInstance instance_type;
898 InstanceSet m_instances;
899 RemapWrapper m_remapwrapper;
900 public:
901 RemapWrapperNode( const char* name ) : m_node( this ), m_remapwrapper( name ){
902         m_node.m_instance = this;
903 }
904
905 void release(){
906         delete this;
907 }
908 scene::Node& node(){
909         return m_node;
910 }
911
912 scene::Instance* create( const scene::Path& path, scene::Instance* parent ){
913         return new instance_type( path, parent, m_remapwrapper );
914 }
915 void forEachInstance( const scene::Instantiable::Visitor& visitor ){
916         m_instances.forEachInstance( visitor );
917 }
918 void insert( scene::Instantiable::Observer* observer, const scene::Path& path, scene::Instance* instance ){
919         m_instances.insert( observer, path, instance );
920 }
921 scene::Instance* erase( scene::Instantiable::Observer* observer, const scene::Path& path ){
922         return m_instances.erase( observer, path );
923 }
924 };
925
926 scene::Node& LoadRemapModel( const char* name ){
927         return ( new RemapWrapperNode( name ) )->node();
928 }
929
930 #endif
931
932
933 size_t picoInputStreamReam( void* inputStream, unsigned char* buffer, size_t length ){
934         return reinterpret_cast<InputStream*>( inputStream )->read( buffer, length );
935 }
936
937 scene::Node& loadPicoModel( const picoModule_t* module, ArchiveFile& file ){
938         picoModel_t* model = PicoModuleLoadModelStream( module, &file.getInputStream(), picoInputStreamReam, file.size(), 0, file.getName() );
939         PicoModelNode* modelNode = new PicoModelNode( model );
940         PicoFreeModel( model );
941         return modelNode->node();
942 }