q3bsp + aufbau der lightmaps



  • moin,
    ich wollte meinem loader für quake 3 levels gerade unterstützung für lightmaps verpassen, allerdings werd ich aus dem aufbau des lumps nicht ganz schlau.

    lightmap
        ubyte[128][128][3] map
    

    wie genau wird da die textur gespeichert?



  • nun.. sicher bin ich mir nicht, aber das stinkt dich nach einer 128x128 textur mit jewals 8bit pro kanal^^
    also währe das lightmap [höhe][breite][RGB] (welche reihenfolge höhe/breite.. kp)
    für RGB den Kanal angeben(weis wieder nicht, ob es sich dabei um RGB/RBG handelt..)
    Also wenn du den R wert des pixels(125,10) haben willst bekommst du den über
    ligthmap[124][9][0]
    wenn du G haben wilst währe das also:
    ligthmap[124][9][1]
    wenn du das ganze in nem File hast, kanns du es auch als array[höhe*breite*3] interpretieren, dann grefist du auf R von pixel (125,10) mittels [125*128*3+9*3+1] zu also [pixel.y*breite*3+pixel.x*3+Kanal]
    greetz TGM



  • Programmierst du in OpenGL? Die Lightmap Daten liegen so im bsp, wie sie auch OpenGL zur Texturerstellung brauch.

    Auszug aus meinem Loader in Java (hab bisschen was ausgelassen)

    public static int generateLightmap(int[] lightmapData) {
    
    		IntBuffer tmp = BufferUtils.createIntBuffer(1);
    
    		// Generate the texture identifiers
    		GL11.glGenTextures(tmp);
    		int textureID = tmp.get(0);
    
    		// Is lightmap data provided?
    		if (lightmapData != null) {
    
    			// Change the gamma settings on the lightmaps (make them brighter)
    			// [...]
    			}
    
    			// Generate texture in OpenGL
    			GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID);
    
    			ByteBuffer data = BufferUtils.createByteBuffer(128 * 128 * 3);
    
    			for (int i = 0; i < 128 * 128 * 3; i++)
    				data.put(i, (byte) lightmapData[i]);
    
    			GLU.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D, GL11.GL_RGB, 128, 128, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, data);
    
    			// Set Parameters
    			GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_LINEAR);
    			GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);			
    			GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
    			GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
    
    		} else { // Parameter = null  ->  generate white image
    
    			// Create white texture if no lightmap specified
    			GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID);
    
    			ByteBuffer white = BufferUtils.createByteBuffer(4);
    			for (int i = 0; i < 4; i++)
    				white.put(i, (byte) 255);
    
    			// Create texture
    			GLU.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D, GL11.GL_RGB, 1, 1, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, white);
    
    			// Set Parameters
    			GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_LINEAR);
    			GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
    			GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
    			GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
    		}
    
    		return textureID;
    	}
    


  • Genau, GL_RGB mit GL_UNSIGNED_BYTE ist das Format.

    Ansonsten würde mich mal die Gammaberechnung interessieren. Ich habe es bisher so gemacht, dass ich ein Wert immer zu den einzelnen Komponenten hinzuaddiert habe und dann auf Unter- und Überlauf geprüft habe:

    R =+ X;
    G =+ X;
    B =+ X;
    if(R < 0) R = 0;
    if(R > 255) R = 255;
    if(G < 0) G = 0;
    if(G > 255) G = 255;
    if(B < 0) B = 0;
    if(B > 255) B = 255;
    

    Wird in Quake3 vllt. mit HSV gearbeitet, und die Gammakorrektur über die Komponente Value(Grauwert/Helligkeit) geregelt?

    mfg olli



  • ok danke



  • Vertex schrieb:

    Gammaberechnung

    Ich weiß gar nicht mehr wo ich's herhab, aber im Internet hatte ich was gefunden:

    float r,g,b;
    	float gamma = 2.5;
    
    	for (int y = 0; y < 128; y++) {
     			for (int x = 0; x < 128; x++) {
    
    				r = fileLightmaps[i].lightmap[y][x][0];
    				g = fileLightmaps[i].lightmap[y][x][1];
    				b = fileLightmaps[i].lightmap[y][x][2];
    
    				r*=gamma/255.0f;
    				g*=gamma/255.0f;
    				b*=gamma/255.0f;
    
    				//find the value to scale back up
    				float scale=1.0f;
    				float temp;
    				if(r > 1.0f && (temp = (1.0f/r)) < scale) scale=temp;
    				if(g > 1.0f && (temp = (1.0f/g)) < scale) scale=temp;
    				if(b > 1.0f && (temp = (1.0f/b)) < scale) scale=temp;
    
    				// scale up color values
    				scale*=255.0f;		
    				r*=scale;
    				g*=scale;
    				b*=scale;
    }
    }
    

    Multitexturing solltest du dann auf Modulate2x stellen, dass es schön aussieht. Ich hatte auch einen Vergleichsscreenshot gemacht: http://bloody-blades.de/images/modulateComp.jpg
    (Man ignoriere den hässlichen Skybox-Platzhalter :P)

    Alternativ kannst du auch einfach im Quake 3 Source gucken 🙂



  • Da bitte, orginal Carmack'scher Code:

    //===============================================================================
    
    static void HSVtoRGB( float h, float s, float v, float rgb[3] )
    {
    	int i;
    	float f;
    	float p, q, t;
    
    	h *= 5;
    
    	i = floor( h );
    	f = h - i;
    
    	p = v * ( 1 - s );
    	q = v * ( 1 - s * f );
    	t = v * ( 1 - s * ( 1 - f ) );
    
    	switch ( i )
    	{
    	case 0:
    		rgb[0] = v;
    		rgb[1] = t;
    		rgb[2] = p;
    		break;
    	case 1:
    		rgb[0] = q;
    		rgb[1] = v;
    		rgb[2] = p;
    		break;
    	case 2:
    		rgb[0] = p;
    		rgb[1] = v;
    		rgb[2] = t;
    		break;
    	case 3:
    		rgb[0] = p;
    		rgb[1] = q;
    		rgb[2] = v;
    		break;
    	case 4:
    		rgb[0] = t;
    		rgb[1] = p;
    		rgb[2] = v;
    		break;
    	case 5:
    		rgb[0] = v;
    		rgb[1] = p;
    		rgb[2] = q;
    		break;
    	}
    }
    
    /*
    ===============
    R_ColorShiftLightingBytes
    
    ===============
    */
    static	void R_ColorShiftLightingBytes( byte in[4], byte out[4] ) {
    	int		shift, r, g, b;
    
    	// shift the color data based on overbright range
    	shift = r_mapOverBrightBits->integer - tr.overbrightBits;
    
    	// shift the data based on overbright range
    	r = in[0] << shift;
    	g = in[1] << shift;
    	b = in[2] << shift;
    
    	// normalize by color instead of saturating to white
    	if ( ( r | g | b ) > 255 ) {
    		int		max;
    
    		max = r > g ? r : g;
    		max = max > b ? max : b;
    		r = r * 255 / max;
    		g = g * 255 / max;
    		b = b * 255 / max;
    	}
    
    	out[0] = r;
    	out[1] = g;
    	out[2] = b;
    	out[3] = in[3];
    }
    
    /*
    ===============
    R_LoadLightmaps
    
    ===============
    */
    #define	LIGHTMAP_SIZE	128
    static	void R_LoadLightmaps( lump_t *l ) {
    	byte		*buf, *buf_p;
    	int			len;
    	MAC_STATIC byte		image[LIGHTMAP_SIZE*LIGHTMAP_SIZE*4];
    	int			i, j;
    	float maxIntensity = 0;
    	double sumIntensity = 0;
    
        len = l->filelen;
    	if ( !len ) {
    		return;
    	}
    	buf = fileBase + l->fileofs;
    
    	// we are about to upload textures
    	R_SyncRenderThread();
    
    	// create all the lightmaps
    	tr.numLightmaps = len / (LIGHTMAP_SIZE * LIGHTMAP_SIZE * 3);
    	if ( tr.numLightmaps == 1 ) {
    		//FIXME: HACK: maps with only one lightmap turn up fullbright for some reason.
    		//this avoids this, but isn't the correct solution.
    		tr.numLightmaps++;
    	}
    
    	// if we are in r_vertexLight mode, we don't need the lightmaps at all
    	if ( r_vertexLight->integer || glConfig.hardwareType == GLHW_PERMEDIA2 ) {
    		return;
    	}
    
    	for ( i = 0 ; i < tr.numLightmaps ; i++ ) {
    		// expand the 24 bit on-disk to 32 bit
    		buf_p = buf + i * LIGHTMAP_SIZE*LIGHTMAP_SIZE * 3;
    
    		if ( r_lightmap->integer == 2 )
    		{	// color code by intensity as development tool	(FIXME: check range)
    			for ( j = 0; j < LIGHTMAP_SIZE * LIGHTMAP_SIZE; j++ )
    			{
    				float r = buf_p[j*3+0];
    				float g = buf_p[j*3+1];
    				float b = buf_p[j*3+2];
    				float intensity;
    				float out[3];
    
    				intensity = 0.33f * r + 0.685f * g + 0.063f * b;
    
    				if ( intensity > 255 )
    					intensity = 1.0f;
    				else
    					intensity /= 255.0f;
    
    				if ( intensity > maxIntensity )
    					maxIntensity = intensity;
    
    				HSVtoRGB( intensity, 1.00, 0.50, out );
    
    				image[j*4+0] = out[0] * 255;
    				image[j*4+1] = out[1] * 255;
    				image[j*4+2] = out[2] * 255;
    				image[j*4+3] = 255;
    
    				sumIntensity += intensity;
    			}
    		} else {
    			for ( j = 0 ; j < LIGHTMAP_SIZE * LIGHTMAP_SIZE; j++ ) {
    				R_ColorShiftLightingBytes( &buf_p[j*3], &image[j*4] );
    				image[j*4+3] = 255;
    			}
    		}
    		tr.lightmaps[i] = R_CreateImage( va("*lightmap%d",i), image, 
    			LIGHTMAP_SIZE, LIGHTMAP_SIZE, qfalse, qfalse, GL_CLAMP );
    	}
    
    	if ( r_lightmap->integer == 2 )	{
    		ri.Printf( PRINT_ALL, "Brightest lightmap value: %d\n", ( int ) ( maxIntensity * 255 ) );
    	}
    }
    

Anmelden zum Antworten