mirror of
https://github.com/FWGS/xash3d-fwgs.git
synced 2026-08-05 03:24:56 +08:00
public: move atlas packing code from renderer, add tests
This commit is contained in:
62
public/atlas.c
Normal file
62
public/atlas.c
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
atlas.c - generic 2D atlas packer (strip-based)
|
||||
Copyright (C) 1997 id Software
|
||||
Copyright (C) Xash3D and Xash3D FWGS developers
|
||||
|
||||
This program 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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.
|
||||
*/
|
||||
#include "atlas.h"
|
||||
|
||||
qboolean Atlas_AllocBlock( atlas_t *atlas, int w, int h, int *x, int *y )
|
||||
{
|
||||
int i, j;
|
||||
int best, best2;
|
||||
int size = atlas->size;
|
||||
|
||||
best = size;
|
||||
|
||||
for( i = 0; i <= size - w; )
|
||||
{
|
||||
best2 = 0;
|
||||
|
||||
for( j = 0; j < w; j++ )
|
||||
{
|
||||
if( atlas->allocated[i + j] >= best )
|
||||
break;
|
||||
if( atlas->allocated[i + j] > best2 )
|
||||
best2 = atlas->allocated[i + j];
|
||||
}
|
||||
|
||||
if( j == w )
|
||||
{
|
||||
*x = i;
|
||||
*y = best = best2;
|
||||
if( best == 0 )
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
i += j + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if( best + h > size )
|
||||
return false;
|
||||
|
||||
for( i = 0; i < w; i++ )
|
||||
atlas->allocated[*x + i] = best + h;
|
||||
|
||||
if( best + h > atlas->max_height )
|
||||
atlas->max_height = best + h;
|
||||
|
||||
return true;
|
||||
}
|
||||
41
public/atlas.h
Normal file
41
public/atlas.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
atlas.h - generic 2D atlas packer (strip-based)
|
||||
Copyright (C) 1997 id Software
|
||||
Copyright (C) Xash3D and Xash3D FWGS developers
|
||||
|
||||
This program 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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.
|
||||
*/
|
||||
#ifndef ATLAS_H
|
||||
#define ATLAS_H
|
||||
|
||||
#include <string.h>
|
||||
#include "xash3d_types.h"
|
||||
|
||||
// maximum atlas dimension
|
||||
#define ATLAS_MAX_SIZE 1024
|
||||
|
||||
typedef struct atlas_s
|
||||
{
|
||||
int allocated[ATLAS_MAX_SIZE];
|
||||
int size;
|
||||
int max_height;
|
||||
} atlas_t;
|
||||
|
||||
static inline void Atlas_Init( atlas_t *atlas, int size )
|
||||
{
|
||||
memset( atlas->allocated, 0, sizeof( atlas->allocated ));
|
||||
atlas->size = size;
|
||||
atlas->max_height = 0;
|
||||
}
|
||||
|
||||
qboolean Atlas_AllocBlock( atlas_t *atlas, int w, int h, int *x, int *y );
|
||||
|
||||
#endif // ATLAS_H
|
||||
131
public/tests/test_atlas.c
Normal file
131
public/tests/test_atlas.c
Normal file
@@ -0,0 +1,131 @@
|
||||
#include "atlas.h"
|
||||
|
||||
static int Test_BasicAlloc( void )
|
||||
{
|
||||
atlas_t atlas;
|
||||
int x, y;
|
||||
|
||||
Atlas_Init( &atlas, 128 );
|
||||
|
||||
if( !Atlas_AllocBlock( &atlas, 16, 16, &x, &y ))
|
||||
return 1;
|
||||
|
||||
if( x != 0 || y != 0 )
|
||||
return 2;
|
||||
|
||||
if( atlas.max_height != 16 )
|
||||
return 3;
|
||||
|
||||
if( !Atlas_AllocBlock( &atlas, 16, 16, &x, &y ))
|
||||
return 4;
|
||||
|
||||
if( x != 16 || y != 0 )
|
||||
return 5;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Test_Full( void )
|
||||
{
|
||||
atlas_t atlas;
|
||||
int x, y;
|
||||
|
||||
Atlas_Init( &atlas, 64 );
|
||||
|
||||
if( !Atlas_AllocBlock( &atlas, 64, 64, &x, &y ))
|
||||
return 1;
|
||||
|
||||
if( Atlas_AllocBlock( &atlas, 1, 1, &x, &y ))
|
||||
return 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Test_Packing( void )
|
||||
{
|
||||
atlas_t atlas;
|
||||
int x, y;
|
||||
|
||||
Atlas_Init( &atlas, 64 );
|
||||
|
||||
if( !Atlas_AllocBlock( &atlas, 32, 16, &x, &y ))
|
||||
return 1;
|
||||
|
||||
if( !Atlas_AllocBlock( &atlas, 32, 16, &x, &y ))
|
||||
return 2;
|
||||
|
||||
if( x != 32 || y != 0 )
|
||||
return 3;
|
||||
|
||||
if( !Atlas_AllocBlock( &atlas, 64, 16, &x, &y ))
|
||||
return 4;
|
||||
|
||||
if( y != 16 )
|
||||
return 5;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Test_Reset( void )
|
||||
{
|
||||
atlas_t atlas;
|
||||
int x, y;
|
||||
|
||||
Atlas_Init( &atlas, 64 );
|
||||
|
||||
if( !Atlas_AllocBlock( &atlas, 64, 64, &x, &y ))
|
||||
return 1;
|
||||
|
||||
Atlas_Init( &atlas, 64 );
|
||||
|
||||
if( !Atlas_AllocBlock( &atlas, 64, 64, &x, &y ))
|
||||
return 2;
|
||||
|
||||
if( x != 0 || y != 0 )
|
||||
return 3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Test_TooLarge( void )
|
||||
{
|
||||
atlas_t atlas;
|
||||
int x, y;
|
||||
|
||||
Atlas_Init( &atlas, 64 );
|
||||
|
||||
if( Atlas_AllocBlock( &atlas, 65, 1, &x, &y ))
|
||||
return 1;
|
||||
|
||||
if( Atlas_AllocBlock( &atlas, 1, 65, &x, &y ))
|
||||
return 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main( void )
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = Test_BasicAlloc();
|
||||
if( ret > 0 )
|
||||
return ret;
|
||||
|
||||
ret = Test_Full();
|
||||
if( ret > 0 )
|
||||
return ret + 16;
|
||||
|
||||
ret = Test_Packing();
|
||||
if( ret > 0 )
|
||||
return ret + 32;
|
||||
|
||||
ret = Test_Reset();
|
||||
if( ret > 0 )
|
||||
return ret + 48;
|
||||
|
||||
ret = Test_TooLarge();
|
||||
if( ret > 0 )
|
||||
return ret + 64;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -157,6 +157,7 @@ def build(bld):
|
||||
'efp': 'tests/test_efp.c',
|
||||
'atoi': 'tests/test_atoi.c',
|
||||
'parsefile': 'tests/test_parsefile.c',
|
||||
'atlas': 'tests/test_atlas.c',
|
||||
}
|
||||
|
||||
for i in tests:
|
||||
|
||||
@@ -16,6 +16,7 @@ GNU General Public License for more details.
|
||||
#include "gl_local.h"
|
||||
#include "xash3d_mathlib.h"
|
||||
#include "mod_local.h"
|
||||
#include "atlas.h"
|
||||
|
||||
#define TURBSCALE ( 256.0f / ( M_PI2 ))
|
||||
|
||||
@@ -27,8 +28,7 @@ static const float r_turbsin[] =
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int allocated[BLOCK_SIZE_MAX];
|
||||
int max_height; // maximum height currently in use in the block
|
||||
atlas_t atlas;
|
||||
int current_lightmap_texture;
|
||||
msurface_t *dynamic_surfaces;
|
||||
msurface_t *lightmap_surfaces[MAX_LIGHTMAPS];
|
||||
@@ -646,60 +646,17 @@ static void R_AddDynamicLights( const msurface_t *surf )
|
||||
*/
|
||||
static void LM_InitBlock( void )
|
||||
{
|
||||
memset( gl_lms.allocated, 0, sizeof( gl_lms.allocated ));
|
||||
gl_lms.max_height = 0;
|
||||
Atlas_Init( &gl_lms.atlas, BLOCK_SIZE );
|
||||
}
|
||||
|
||||
static int LM_AllocBlock( int w, int h, int *x, int *y )
|
||||
static qboolean LM_AllocBlock( int w, int h, int *x, int *y )
|
||||
{
|
||||
int i, j;
|
||||
int best, best2;
|
||||
|
||||
best = BLOCK_SIZE;
|
||||
|
||||
for( i = 0; i < BLOCK_SIZE - w; )
|
||||
{
|
||||
best2 = 0;
|
||||
|
||||
for( j = 0; j < w; j++ )
|
||||
{
|
||||
if( gl_lms.allocated[i+j] >= best )
|
||||
break;
|
||||
if( gl_lms.allocated[i+j] > best2 )
|
||||
best2 = gl_lms.allocated[i+j];
|
||||
}
|
||||
|
||||
if( j == w )
|
||||
{
|
||||
// this is a valid spot
|
||||
*x = i;
|
||||
*y = best = best2;
|
||||
if( best == 0 )
|
||||
break; // height 0 is optimal, can't do better
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// allocated[i+j] was too tall — no position in [i, i+j] can work
|
||||
i += j + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if( best + h > BLOCK_SIZE )
|
||||
return false;
|
||||
|
||||
for( i = 0; i < w; i++ )
|
||||
gl_lms.allocated[*x + i] = best + h;
|
||||
|
||||
if( best + h > gl_lms.max_height )
|
||||
gl_lms.max_height = best + h;
|
||||
|
||||
return true;
|
||||
return Atlas_AllocBlock( &gl_lms.atlas, w, h, x, y );
|
||||
}
|
||||
|
||||
static void LM_UploadDynamicBlock( void )
|
||||
{
|
||||
pglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, BLOCK_SIZE, gl_lms.max_height, GL_RGBA, GL_UNSIGNED_BYTE, gl_lms.lightmap_buffer );
|
||||
pglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, BLOCK_SIZE, gl_lms.atlas.max_height, GL_RGBA, GL_UNSIGNED_BYTE, gl_lms.lightmap_buffer );
|
||||
}
|
||||
|
||||
static void LM_UploadBlock( qboolean dynamic )
|
||||
|
||||
Reference in New Issue
Block a user