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