/* =========================================================================== Copyright (C) 2023 the OpenMoHAA team This file is part of OpenMoHAA source code. OpenMoHAA source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. OpenMoHAA source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenMoHAA source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // DESCRIPTION: // light style code #include "cg_local.h" /* =================================================================== LIGHT STYLE CODE =================================================================== */ #define MAX_LIGHTSTYLE_LENGTH 128 #define MAX_LIGHTSTYLE_NAME_LENGTH 64 typedef struct { char name[ MAX_LIGHTSTYLE_NAME_LENGTH ]; qboolean hasalpha; int length; float map[ MAX_LIGHTSTYLE_LENGTH ][ 4 ]; } clightstyle_t; clightstyle_t cg_lightstyle[ MAX_LIGHTSTYLES * 2 ]; /* ================ CG_LightStyleColor ================ */ qboolean CG_LightStyleColor( int style, int realtime, float color[4], qboolean clamp ) { clightstyle_t *ls; int i, time; float frac; qboolean at_end; time = realtime / 50; frac = ( realtime - ( time * 50.0f ) ) / 50.0f; memset( color, 0, sizeof( color ) ); if ( ( style < 0 ) || ( style >= ( MAX_LIGHTSTYLES * 2 ) ) ) { cgi.DPrintf ("CG_LightStyleColor: style out of range.\n"); return qtrue; } ls = &cg_lightstyle[ style ]; if ( !ls->length ) { cgi.DPrintf ("CG_LightStyleColor: style %d has zero length.\n", style); return qtrue; } // by default we are not at the end at_end = qfalse; if ( clamp ) { if ( time >= ls->length ) { time = ls->length - 1; at_end = qtrue; } } else { time %= ls->length; } // // only lerp if we are before the end // if ( time == ls->length - 1 ) { memcpy( color, ls->map[ time ], 4 * sizeof( ls->map[ time ][ 0 ] ) ); } else { for( i = 0; i < 4; i++ ) { color[ i ] = ls->map[ time ][ i ] + frac * ( ls->map[ time + 1 ][ i ] - ls->map[ time ][ i ] ); } } if ( !ls->hasalpha ) color[ 3 ] = 1; return at_end; } /* ============= LoadTGA ============= */ static qboolean LoadTGA(const char * name, byte ** pic, int *width, int *height, qboolean * hasalpha ) { int columns, rows, numPixels; byte *pixbuf; int row, column; byte *buf_p; byte *buffer; TargaHeader targa_header; byte *targa_rgba; byte targa_palette[ 256 * 4 ]; int i; *pic = NULL; *hasalpha = false; // // load the file // cgi.FS_ReadFile(name, (void **)&buffer, qtrue); if (!buffer) { cgi.DPrintf( "TGA File not found: %s\n", name ); return false; } buf_p = buffer; targa_header.id_length = *buf_p++; targa_header.colormap_type = *buf_p++; targa_header.image_type = *buf_p++; targa_header.colormap_index = LittleShort ( *((short *)buf_p) ); buf_p += 2; targa_header.colormap_length = LittleShort ( *((short *)buf_p) ); buf_p += 2; targa_header.colormap_size = *buf_p++ / 8; targa_header.x_origin = LittleShort ( *((short *)buf_p) ); buf_p += 2; targa_header.y_origin = LittleShort ( *((short *)buf_p) ); buf_p += 2; targa_header.width = LittleShort ( *((short *)buf_p) ); buf_p += 2; targa_header.height = LittleShort ( *((short *)buf_p) ); buf_p += 2; targa_header.pixel_size = *buf_p++; targa_header.attributes = *buf_p++; if (targa_header.image_type!=1 && targa_header.image_type!=2 && targa_header.image_type!=10) { cgi.DPrintf("LoadTGA: Only type 1, 2 and 10 targa RGB images supported\n"); return false; } if ( (targa_header.colormap_type !=0 && targa_header.colormap_type !=1) || (targa_header.pixel_size!=32 && targa_header.pixel_size!=24 && targa_header.pixel_size!=8)) { cgi.DPrintf("LoadTGA: Only 24, 32 and colormap images supported (no colormaps)\n"); return false; } columns = targa_header.width; rows = targa_header.height; numPixels = columns * rows; if (width) *width = columns; if (height) *height = rows; targa_rgba = ( byte * )cgi.Malloc( numPixels * 4 ); *pic = targa_rgba; if (targa_header.id_length != 0) buf_p += targa_header.id_length; // skip TARGA image comment for (i=0;i=0; row--) { pixbuf = targa_rgba + row*columns; for(column=0; column=0; row--) { pixbuf = targa_rgba + row*columns*4; for(column=0; column=0; row--) { pixbuf = targa_rgba + row*columns*4; for(column=0; column0) row--; else goto breakOut; pixbuf = targa_rgba + row*columns*4; } } } else { // non run-length packet for(j=0;j0) row--; else goto breakOut; pixbuf = targa_rgba + row*columns*4; } } } } breakOut:; } } cgi.FS_FreeFile( buffer ); return true; } void createLightstyleFromTGA( int num, const char *filename ) { byte *data=0; int i, j, width, height; qboolean hasalpha; if (!LoadTGA(filename, &data, &width, &height, &hasalpha)) { return; } if ( width > MAX_LIGHTSTYLE_LENGTH ) { cgi.DPrintf("createLightstyleFromTGA: Light style too large, truncating.\n",filename); width = MAX_LIGHTSTYLE_LENGTH; } strncpy( cg_lightstyle[ num ].name, filename, MAX_LIGHTSTYLE_NAME_LENGTH ); cg_lightstyle[ num ].hasalpha = hasalpha; cg_lightstyle[ num ].length = width; for( i = 0; i < width; i++ ) { for ( j = 0; j < 4; j++ ) { cg_lightstyle[ num ].map[ i ][ j ] = ( float )data[ ( i * 4 ) + j ] / 255.0f; } } if ( data ) cgi.Free( data ); } void CG_SetLightStyle( int num, const char *s ) { const char *r, *g, *b, *a; int j, k, n; size_t len; float t; if ( ( num < 0 ) || ( num >= ( MAX_LIGHTSTYLES * 2 ) ) ) { cgi.DPrintf ("CG_SetLightStyle: num out of range.\n"); return; } len = strlen( s ); if ( !strcmpi( &s[ len - 4 ], ".tga" ) ) { createLightstyleFromTGA( num, s ); return; } if ( !s ) return; r = strchr( s, 'R' ); g = strchr( s, 'G' ); b = strchr( s, 'B' ); a = strchr( s, 'A' ); n = 0; if (r) n++; if (g) n++; if (b) n++; if (a) n++; if (!n) n++; if (!a) cg_lightstyle[ num ].hasalpha = false; else cg_lightstyle[ num ].hasalpha = true; j = strlen (s) / n; if (j >= MAX_LIGHTSTYLE_LENGTH) { cgi.DPrintf ( "svc_lightstyle length=%i", j); j = MAX_LIGHTSTYLE_LENGTH - 1; } if ( !r && !g && !b && !a ) { cg_lightstyle[ num ].length = j; for( k = 0; k < j; k++ ) { t = ( float )( s[ k ] - 'a' ) * 127.5f / 12.5; if ( t > 255 ) { t = 255.0f; } cg_lightstyle[ num ].map[ k ][ 0 ] = t / 255.0f; cg_lightstyle[ num ].map[ k ][ 1 ] = t / 255.0f; cg_lightstyle[ num ].map[ k ][ 2 ] = t / 255.0f; cg_lightstyle[ num ].map[ k ][ 3 ] = 1; } } else { n = 0; // red k = 0; t = 0; if ( r ) { r++; while( ( r[ k ] >= 'a' ) && ( r[ k ] <= 'z' ) ) { t = ( float )( r[ k ] - 'a' ) * 127.5f / 12.5; if ( t > 255 ) { t = 255.0f; } cg_lightstyle[ num ].map[ k ][ 0 ] = t / 255.0f; k++; } n = k; } while( k < MAX_LIGHTSTYLE_LENGTH ) { cg_lightstyle[ num ].map[ k++ ][ 0 ] = t / 255.0f; } // green k = 0; t = 0; if ( g ) { g++; while( ( g[ k ] >= 'a' ) && ( g[ k ] <= 'z' ) ) { t = ( float )( g[ k ] - 'a' ) * 127.5f / 12.5; if ( t > 255 ) { t = 255.0f; } cg_lightstyle[ num ].map[ k ][ 1 ] = t / 255.0f; k++; } n = max( n, k ); } while( k < MAX_LIGHTSTYLE_LENGTH ) { cg_lightstyle[ num ].map[ k++ ][ 1 ] = t / 255.0f; } // blue k = 0; t = 0; if ( b ) { b++; while( ( b[ k ] >= 'a' ) && ( b[ k ] <= 'z' ) ) { t = ( float )( b[ k ] - 'a' ) * 127.5f / 12.5; if ( t > 255 ) { t = 255.0f; } cg_lightstyle[ num ].map[ k ][ 2 ] = t / 255.0f; k++; } n = max( n, k ); } while( k < MAX_LIGHTSTYLE_LENGTH ) { cg_lightstyle[ num ].map[ k++ ][ 2 ] = t / 255.0f; } // alpha k = 0; t = 255; if ( a ) { a++; while( ( a[ k ] >= 'a' ) && ( a[ k ] <= 'z' ) ) { t = ( float )( a[ k ] - 'a' ) * 127.5f / 25; if ( t > 255 ) { t = 255.0f; } cg_lightstyle[ num ].map[ k ][ 3 ] = t / 255.0f; k++; } n = max( n, k ); } while( k < MAX_LIGHTSTYLE_LENGTH ) { cg_lightstyle[ num ].map[ k++ ][ 3 ] = t / 255.0f; } cg_lightstyle[ num ].length = n; } } /* ================ CG_GetLightStyle ================ */ int CG_GetLightStyle( const char * name ) { int i; for( i = MAX_LIGHTSTYLES; i < MAX_LIGHTSTYLES * 2; i++ ) { if ( !strcmpi( cg_lightstyle[ i ].name, name ) ) { return i; } } return -1; } /* ================ CG_GetFreeLightStyle ================ */ int CG_GetFreeLightStyle( void ) { int i; for( i = MAX_LIGHTSTYLES; i < MAX_LIGHTSTYLES * 2; i++ ) { if ( !cg_lightstyle[ i ].name[ 0 ] ) { return i; } } return -1; } /* ================ CG_ClearLightStyles ================ */ void CG_ClearLightStyles( void ) { memset( cg_lightstyle, 0, sizeof( cg_lightstyle ) ); } /* ================ CG_RegisterLightStyle ================ */ int CG_RegisterLightStyle( const char * name ) { int num; num = CG_GetLightStyle( name ); if ( num == -1 ) { num = CG_GetFreeLightStyle(); if ( num != -1 ) { CG_SetLightStyle( num, name ); } else { cgi.DPrintf ("CG_RegisterLightStyle: no free spots for lightstyle %s.\n"); } } return num; }