Arachnoid GLESv1.1 plugin. Compile and run (a bit glitchy and no frameskip) on the...
[mupen64plus-pandora.git] / source / mupen64plus-video-arachnoid / src / texture / TextureCache.cpp
1 /******************************************************************************
2  * Arachnoid Graphics Plugin for Mupen64Plus
3  * http://bitbucket.org/wahrhaft/mupen64plus-video-arachnoid/
4  *
5  * Copyright (C) 2007 Kristofer Karlsson, Rickard Niklasson
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program 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 this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20  *****************************************************************************/
21
22 #include "TextureCache.h"
23
24 #include "RDP.h"
25 #include "RSP.h"
26 #include "CachedTexture.h"
27 #include "MathLib.h"
28 #include <algorithm>
29     using std::min;
30 #include "m64p.h"
31 #include "OpenGL.h"
32 #include "Memory.h"
33 #include "OpenGLRenderer.h"
34 #include "MultiTexturingExt.h"    
35     //gSPBgRect1Cyc
36 //gSPBgRectCopy
37 #define GL_CLAMP_TO_EDGE                  0x812F
38 #define GL_GENERATE_MIPMAP                0x8191
39
40 #include "Logger.h"
41 #include <iostream>
42
43 //-----------------------------------------------------------------------------
44 //! Constructor
45 //-----------------------------------------------------------------------------
46 TextureCache::TextureCache()
47 {
48     m_currentTextures[0] = 0;
49     m_currentTextures[1] = 0;
50 }
51
52 //-----------------------------------------------------------------------------
53 //! Destructor
54 //-----------------------------------------------------------------------------
55 TextureCache::~TextureCache()
56 {   
57     dispose();
58 }
59
60 //-----------------------------------------------------------------------------
61 //* Initialize
62 //-----------------------------------------------------------------------------
63 bool TextureCache::initialize(RSP* rsp, RDP* rdp, Memory* memory, unsigned int textureBitDepth, unsigned int cacheSize)
64 {
65     m_rsp = rsp;
66     m_rdp = rdp;
67     m_memory   = memory;
68     m_bitDepth = textureBitDepth;
69     m_maxBytes = cacheSize;
70     
71     return true;
72 }
73
74 //-----------------------------------------------------------------------------
75 //* Update
76 //-----------------------------------------------------------------------------
77 void TextureCache::update(unsigned int tile)
78 {
79     //if (cache.bitDepth != OGL.textureBitDepth)
80     //{
81     //    TextureCache_Destroy();
82     //    TextureCache_Init();
83     //}
84
85     //Special textures?
86     if ( m_rdp->getTextureMode() == TM_BGIMAGE )
87     {
88         return;
89     }
90     else if ( m_rdp->getTextureMode() == TM_FRAMEBUFFER )
91     {
92         return;
93     }
94
95
96     CachedTexture temp;    
97     unsigned int maskWidth = 0, maskHeight = 0;
98     _calculateTextureSize(tile, &temp, maskWidth, maskHeight);
99
100     static int hits = 0;
101     static int misses = 0;
102
103     //For each texture in texture cache
104     for (TextureList::iterator it=m_cachedTextures.begin(); it!=m_cachedTextures.end(); ++it)
105       {
106         CachedTexture* temp2 = (*it);
107
108         if ( *temp2 == temp )
109         {
110             _activateTexture( tile, (*it) );
111             hits++;
112             return;
113         }
114     }
115     misses++;
116
117     // If multitexturing, set the appropriate texture
118     //if (OGL.ARB_multitexture)
119     glActiveTextureARB( GL_TEXTURE0_ARB + tile );
120
121     //Add new texture to cache
122     m_currentTextures[tile] = addTop();
123     m_currentTextures[tile]->activate();
124
125     m_currentTextures[tile]->address     = m_rdp->getTextureImage()->address;
126     m_currentTextures[tile]->crc         = temp.crc;
127     m_currentTextures[tile]->width       = temp.width;
128     m_currentTextures[tile]->height      = temp.height;
129     m_currentTextures[tile]->clampWidth  = temp.clampWidth;
130     m_currentTextures[tile]->clampHeight = temp.clampHeight;
131
132     m_currentTextures[tile]->format = m_rsp->getTile(tile)->format;
133     m_currentTextures[tile]->size =  m_rsp->getTile(tile)->size;
134
135     m_currentTextures[tile]->palette = m_rsp->getTile(tile)->palette;
136 /*    m_currentTextures[tile]->fulS = rsp.getTile(tile)->fulS;
137     m_currentTextures[tile]->fulT = rsp.getTile(tile)->fulT;
138     m_currentTextures[tile]->ulS = rsp.getTile(tile)->ulS;
139     m_currentTextures[tile]->ulT = rsp.getTile(tile)->ulT;
140     m_currentTextures[tile]->lrS = rsp.getTile(tile)->lrS;
141     m_currentTextures[tile]->lrT = rsp.getTile(tile)->lrT;*/
142     m_currentTextures[tile]->maskS   = m_rsp->getTile(tile)->masks;
143     m_currentTextures[tile]->maskT   = m_rsp->getTile(tile)->maskt;
144     m_currentTextures[tile]->mirrorS = m_rsp->getTile(tile)->mirrors;
145     m_currentTextures[tile]->mirrorT = m_rsp->getTile(tile)->mirrort;
146     m_currentTextures[tile]->clampS  = m_rsp->getTile(tile)->clamps;
147     m_currentTextures[tile]->clampT  = m_rsp->getTile(tile)->clampt;
148     m_currentTextures[tile]->line    = m_rsp->getTile(tile)->line;
149     m_currentTextures[tile]->tMem    = m_rsp->getTile(tile)->tmem;
150
151
152     
153 //    cache.current[tile]->lastDList = RSP.DList;
154 //    cache.current[tile]->frameBufferTexture = FALSE;
155
156     //Calculate Real Width
157     if (m_currentTextures[tile]->clampS) 
158     {
159         m_currentTextures[tile]->realWidth = pow2( temp.clampWidth );
160     }
161     else if (m_currentTextures[tile]->mirrorS)
162     {
163         m_currentTextures[tile]->realWidth = maskWidth << 1;
164     }
165     else
166     {
167         m_currentTextures[tile]->realWidth = pow2( temp.width );
168     }
169
170     //Calculate Real Height
171     if (m_currentTextures[tile]->clampT)
172     {
173         m_currentTextures[tile]->realHeight = pow2( temp.clampHeight );
174     }
175     else if (m_currentTextures[tile]->mirrorT)
176     {
177         m_currentTextures[tile]->realHeight = maskHeight << 1;
178     }
179     else
180     {
181         m_currentTextures[tile]->realHeight = pow2( temp.height );
182     }
183
184     //Calculate Scale
185     m_currentTextures[tile]->scaleS = 1.0f / (float)(m_currentTextures[tile]->realWidth);
186     m_currentTextures[tile]->scaleT = 1.0f / (float)(m_currentTextures[tile]->realHeight);
187     m_currentTextures[tile]->shiftScaleS = 1.0f;
188     m_currentTextures[tile]->shiftScaleT= 1.0f;
189     #if 0
190         //m_currentTextures[tile]->offsetS = OGL.enable2xSaI ? 0.25f : 0.5f;
191         //m_currentTextures[tile]->offsetT = OGL.enable2xSaI ? 0.25f : 0.5f;
192     #else
193         m_currentTextures[tile]->offsetS = 0.5f;
194         m_currentTextures[tile]->offsetT = 0.5f;    
195     #endif
196
197     if (m_rsp->getTile(tile)->shifts > 10)
198         m_currentTextures[tile]->shiftScaleS = (float)(1 << (16 - m_rsp->getTile(tile)->shifts));
199     else if (m_rsp->getTile(tile)->shifts > 0)
200         m_currentTextures[tile]->shiftScaleS /= (float)(1 << m_rsp->getTile(tile)->shifts);
201
202     if (m_rsp->getTile(tile)->shiftt > 10)
203         m_currentTextures[tile]->shiftScaleT = (float)(1 << (16 - m_rsp->getTile(tile)->shiftt));
204     else if (m_rsp->getTile(tile)->shiftt > 0)
205         m_currentTextures[tile]->shiftScaleT /= (float)(1 << m_rsp->getTile(tile)->shiftt);
206
207
208
209
210
211     _loadTexture( m_currentTextures[tile] );
212     _activateTexture( tile, m_currentTextures[tile] );
213
214     m_cachedBytes += m_currentTextures[tile]->getTextureSize();
215
216 }
217
218 //-----------------------------------------------------------------------------
219 //* Add Top
220 //! Adds a texture to cache
221 //-----------------------------------------------------------------------------
222 CachedTexture* TextureCache::addTop()
223 {
224     //If no memory left, remove old textures from cache
225     while ( m_cachedBytes > m_maxBytes )
226     {
227         this->removeBottom();
228     }
229
230     //Allocate memory
231     CachedTexture* newTexture = new CachedTexture();
232
233     //Generate a texture
234     glGenTextures(1, &newTexture->m_id);
235
236     //Add Texture to cache
237     m_cachedTextures.push_front(newTexture);
238
239     return newTexture;
240 }
241
242 //-----------------------------------------------------------------------------
243 // Remove Bottom
244 //-----------------------------------------------------------------------------
245 void TextureCache::removeBottom() 
246 {
247     //Get Last Texture in list
248     CachedTexture* lastTexture = *(--m_cachedTextures.end());
249
250     //Remove Texture
251     m_cachedTextures.pop_back();
252     m_cachedBytes -= lastTexture->getTextureSize();
253
254     //if (cache.bottom->frameBufferTexture)
255     //    FrameBuffer_RemoveBuffer( cache.bottom->address );
256
257     //Delete texture
258     glDeleteTextures(1, &lastTexture->m_id);
259
260     delete lastTexture;
261 }
262
263 //-----------------------------------------------------------------------------
264 // Remove
265 //-----------------------------------------------------------------------------
266 void TextureCache::remove( CachedTexture *texture ) 
267 {
268
269 }
270
271 //-----------------------------------------------------------------------------
272 //Move Texture to top
273 //-----------------------------------------------------------------------------
274 void TextureCache::moveToTop( CachedTexture *newtop ) 
275 {
276     //Get Texture
277     TextureList::iterator it = std::find( m_cachedTextures.begin(), m_cachedTextures.end(), newtop);
278
279     //Erase Texture
280     if ( it != m_cachedTextures.end() )
281     {
282         m_cachedTextures.erase(it);
283     }
284
285     //Add texture to the front of the list
286     m_cachedTextures.push_front(newtop);
287 }
288
289 //-----------------------------------------------------------------------------
290 // Dispose
291 //-----------------------------------------------------------------------------
292 void TextureCache::dispose()
293 {
294     //For each texture
295     for (TextureList::iterator it=m_cachedTextures.begin(); it!=m_cachedTextures.end(); ++it )
296     {
297         delete (*it);
298     }
299     m_cachedTextures.clear();
300 }
301
302 //-----------------------------------------------------------------------------
303 // Load Texture
304 //-----------------------------------------------------------------------------
305 void TextureCache::_loadTexture(CachedTexture* texture)
306 {
307     //Select Image Type
308     GetTexelFunc getTexelFunc;
309     unsigned int internalFormat;
310     int             imageType;
311     m_formatSelector.detectImageFormat(texture, m_bitDepth, getTexelFunc, internalFormat, imageType, m_rdp->getTextureLUT());
312
313     //Allocate memory
314     unsigned int* dest = new unsigned int[ texture->getTextureSize() ];
315
316     //Get Line Size
317     unsigned short line = (unsigned short)texture->line;
318     if (texture->size == G_IM_SIZ_32b)
319         line <<= 1;
320
321     //
322     //Work Your magic
323     //
324
325     unsigned short mirrorSBit, maskSMask, clampSClamp;
326     unsigned short mirrorTBit, maskTMask, clampTClamp;
327
328     if (texture->maskS)
329     {
330         clampSClamp = (unsigned short)(texture->clampS ? texture->clampWidth - 1 : (texture->mirrorS ? (texture->width << 1) - 1 : texture->width - 1));
331         maskSMask = (1 << texture->maskS) - 1;
332         mirrorSBit = texture->mirrorS ? 1 << texture->maskS : 0;
333     }
334     else
335     {
336         clampSClamp = (unsigned short)min( texture->clampWidth, texture->width ) - 1;
337         maskSMask = 0xFFFF;
338         mirrorSBit = 0x0000;
339     }
340
341     if (texture->maskT)
342     {
343         clampTClamp = (unsigned short)(texture->clampT ? texture->clampHeight - 1 : (texture->mirrorT ? (texture->height << 1) - 1: texture->height - 1));
344         maskTMask = (1 << texture->maskT) - 1;
345         mirrorTBit = texture->mirrorT ?    1 << texture->maskT : 0;
346     }
347     else
348     {
349         clampTClamp = (unsigned short)min( texture->clampHeight, texture->height ) - 1;
350         maskTMask = 0xFFFF;
351         mirrorTBit = 0x0000;
352     }
353
354     // Hack for Zelda warp texture
355     if (((texture->tMem << 3) + (texture->width * texture->height << texture->size >> 1)) > 4096)
356         texture->tMem = 0;
357
358     // limit clamp values to min-0 (Perfect Dark has height=0 textures, making negative clamps)
359     if (clampTClamp & 0x8000)
360         clampTClamp = 0;
361     if (clampSClamp & 0x8000)
362         clampSClamp = 0;
363
364     //
365     //Retrive texture from source (TMEM) and copy it to dest
366     //
367
368     unsigned short x, y, i, j, tx, ty;
369
370     unsigned long long* src;
371
372     j = 0;
373     for (y = 0; y < texture->realHeight; y++)
374     {
375         ty = min(y, clampTClamp) & maskTMask;
376
377         if (y & mirrorTBit) {
378             ty ^= maskTMask;
379         }
380
381         //TODO: remove old if new works
382         //src = m_memory->getTextureMemory(texture->tMem) + line * ty;
383         src = m_memory->getTextureMemory((texture->tMem + line * ty) & 511);
384         
385
386         i = (ty & 1) << 1;
387         for (x = 0; x < texture->realWidth; x++)
388         {
389             tx = min(x, clampSClamp) & maskSMask;
390
391             if (x & mirrorSBit)
392                 tx ^= maskSMask;
393
394             if (internalFormat == GL_RGBA8)
395                 ((unsigned int*)dest)[j++] = getTexelFunc( src, tx, i, texture->palette );
396             else
397                 ((unsigned short*)dest)[j++] = getTexelFunc( src, tx, i, texture->palette );
398         }
399     }
400
401     //Send Texture to OpenGL
402 #ifdef HAVE_GLES
403     glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, texture->realWidth, texture->realHeight, 0, GL_RGBA, imageType, dest );
404 #else
405     glTexImage2D( GL_TEXTURE_2D, 0, internalFormat, texture->realWidth, texture->realHeight, 0, GL_RGBA, imageType, dest );
406 #endif
407     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
408     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
409     delete[] dest;
410 }
411
412
413 void TextureCache::_calculateTextureSize(unsigned int tile, CachedTexture* out, unsigned int& maskWidth, unsigned int& maskHeight )
414 {
415     RDPTile* rspTile = m_rsp->getTile(tile);
416
417     //Calculate Tile Size
418     unsigned int tileWidth =  rspTile->getWidth();
419     unsigned int tileHeight = rspTile->getHeight();
420
421     //Get Mask Size
422     maskWidth = 1 << rspTile->masks;
423     maskHeight = 1 << rspTile->maskt;
424
425     //Get Current Tile Size
426     unsigned int loadWidth = m_rdp->getCurrentTile()->getWidth();
427     unsigned int loadHeight = m_rdp->getCurrentTile()->getHeight();
428
429     unsigned int maxTexels = ImageFormatSelector::imageFormats[rspTile->size][rspTile->format].maxTexels;
430
431     //Get Line Width (depending on imageformat)
432     unsigned int lineWidth = rspTile->line << ImageFormatSelector::imageFormats[rspTile->size][rspTile->format].lineShift;
433     unsigned int lineHeight; 
434     if ( lineWidth ) // Don't allow division by zero
435         lineHeight = min( maxTexels / lineWidth, tileHeight );
436     else
437         lineHeight = 0;
438
439     unsigned int width;
440     unsigned int height;
441
442     if ( m_rdp->getTextureMode() == TM_TEXRECT )
443     {
444         unsigned short texRectWidth = (unsigned short)(m_rdp->getTexRectWidth() - rspTile->uls);
445         unsigned short texRectHeight = (unsigned short)(m_rdp->getTexRectHeight() - rspTile->ult);
446
447         if (rspTile->masks && ((maskWidth * maskHeight) <= maxTexels))
448             width = maskWidth;
449         else if ((tileWidth * tileHeight) <= maxTexels)
450             width = tileWidth;
451         else if ((tileWidth * texRectHeight) <= maxTexels)
452             width = tileWidth;
453         else if ((texRectWidth * tileHeight) <= maxTexels)
454             width = m_rdp->getTexRectWidth();
455         else if ((texRectWidth * texRectHeight) <= maxTexels)
456             width = m_rdp->getTexRectWidth();
457         else if (m_rdp->getLoadType() == LOADTYPE_TILE)
458             width = loadWidth;
459         else
460             width = lineWidth;
461
462         if (rspTile->maskt && ((maskWidth * maskHeight) <= maxTexels))
463             height = maskHeight;
464         else if ((tileWidth * tileHeight) <= maxTexels)
465             height = tileHeight;
466         else if ((tileWidth * texRectHeight) <= maxTexels)
467             height = m_rdp->getTexRectHeight();
468         else if ((texRectWidth * tileHeight) <= maxTexels)
469             height = tileHeight;
470         else if ((texRectWidth * texRectHeight) <= maxTexels)
471             height = m_rdp->getTexRectHeight();
472         else if (m_rdp->getLoadType() == LOADTYPE_TILE)
473             height = loadHeight;
474         else
475             height = lineHeight;
476     }
477     else
478     {
479         if (rspTile->masks && ((maskWidth * maskHeight) <= maxTexels))
480             width = maskWidth; // Use mask width if set and valid
481         else if ((tileWidth * tileHeight) <= maxTexels)
482             width = tileWidth; // else use tile width if valid
483         else if (m_rdp->getLoadType() == LOADTYPE_TILE)
484             width = loadWidth; // else use load width if load done with LoadTile
485         else
486             width = lineWidth; // else use line-based width
487
488         if (rspTile->maskt && ((maskWidth * maskHeight) <= maxTexels))
489             height = maskHeight;
490         else if ((tileWidth * tileHeight) <= maxTexels)
491             height = tileHeight;
492         else if (m_rdp->getLoadType() == LOADTYPE_TILE)
493             height = loadHeight;
494         else
495             height = lineHeight;
496     }
497
498      unsigned int clampWidth = rspTile->clamps ? tileWidth : width;
499     unsigned int clampHeight = rspTile->clampt ? tileHeight : height;
500
501     if (clampWidth > 256)
502         rspTile->clamps = 0;
503     if (clampHeight > 256)
504         rspTile->clampt = 0;
505
506     // Make sure masking is valid
507     if (maskWidth > width) 
508     {
509         rspTile->masks = powof( width );
510         maskWidth = 1 << rspTile->masks;
511     }
512
513     if (maskHeight > height)
514     {
515         rspTile->maskt = powof( height );
516         maskHeight = 1 << rspTile->maskt;
517     }
518
519     //Set output data
520     out->width       = width;
521     out->height      = height;
522     out->clampWidth  = clampWidth;
523     out->clampHeight = clampHeight;
524     out->maskS       = m_rsp->getTile(tile)->masks;
525     out->maskT       = m_rsp->getTile(tile)->maskt;
526     out->mirrorS     = m_rsp->getTile(tile)->mirrors;
527     out->mirrorT     = m_rsp->getTile(tile)->mirrort;
528     out->clampS      = m_rsp->getTile(tile)->clamps;
529     out->clampT      = m_rsp->getTile(tile)->clampt;
530     out->format      = m_rsp->getTile(tile)->format;
531     out->size        = m_rsp->getTile(tile)->size; 
532     out->crc         = _calculateCRC(tile, width, height );
533 }
534
535 unsigned int TextureCache::_calculateCRC(unsigned int t, unsigned int width, unsigned int height)
536 {
537     RDPTile* tile = m_rsp->getTile(t);
538
539     unsigned int crc;
540     unsigned int y, bpl, line;
541     unsigned long long *src;
542
543     //TODO: remove if new works
544     //src = m_memory->getTextureMemory(tile->tmem);
545     bpl = width << tile->size >> 1;
546
547     line = tile->line;
548      if (tile->size == G_IM_SIZ_32b)
549         line <<= 1;
550
551     crc = 0xFFFFFFFF;
552      for (y=0; y<height; ++y)
553     {
554         src = m_memory->getTextureMemory((tile->tmem + (y * line)) & 511);
555         crc = m_crcCalculator.calcCRC( crc, src, bpl );
556         //TODO: remove if new works
557         //src += line;
558     }
559
560        if ( tile->format == G_IM_FMT_CI )
561     {
562         if ( tile->size == G_IM_SIZ_4b )
563             crc = m_crcCalculator.calcCRC( crc, &m_rdp->m_paletteCRC16[tile->palette], 4 );
564         else if (tile->size == G_IM_SIZ_8b)
565             crc = m_crcCalculator.calcCRC( crc, &m_rdp->m_paletteCRC256, 4 );
566     }
567     return crc;
568 }
569
570 void TextureCache::_activateTexture( unsigned int t, CachedTexture *texture )
571 {
572     // If multitexturing, set the appropriate texture
573     //if (OGL.ARB_multitexture)
574         glActiveTextureARB( GL_TEXTURE0_ARB + t );
575
576     // Bind the cached texture
577     texture->activate();
578
579     // Set filter mode. Almost always bilinear, but check anyways
580     unsigned int textureFiltering = m_rdp->getTextureFiltering();
581     if ( textureFiltering == G_TF_BILERP || textureFiltering == G_TF_AVERAGE )
582     {
583         if( m_mipmap > 0 )
584         {
585             // Set Mipmap
586             if(m_mipmap == 1)    // nearest
587             {
588                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
589             }
590             else if(m_mipmap == 2)    // bilinear
591             {
592                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
593             }
594             else if(m_mipmap == 3)    // trilinear
595             {
596                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
597             }
598             
599             // Tell to hardware to generate mipmap (himself) when glTexImage2D is called
600             glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
601         }
602         else    // no mipmapping
603         {
604             glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
605             glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE );
606         }
607         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
608         
609     }
610     else
611     {
612         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
613         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
614     }
615
616     
617
618     // Set clamping modes
619     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texture->clampS ? GL_CLAMP_TO_EDGE : GL_REPEAT );
620     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texture->clampT ? GL_CLAMP_TO_EDGE : GL_REPEAT );
621
622     //texture->lastDList = RSP.DList;
623
624     moveToTop( texture );
625
626     m_currentTextures[t] = texture;
627 }