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',
|
'efp': 'tests/test_efp.c',
|
||||||
'atoi': 'tests/test_atoi.c',
|
'atoi': 'tests/test_atoi.c',
|
||||||
'parsefile': 'tests/test_parsefile.c',
|
'parsefile': 'tests/test_parsefile.c',
|
||||||
|
'atlas': 'tests/test_atlas.c',
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in tests:
|
for i in tests:
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ GNU General Public License for more details.
|
|||||||
#include "gl_local.h"
|
#include "gl_local.h"
|
||||||
#include "xash3d_mathlib.h"
|
#include "xash3d_mathlib.h"
|
||||||
#include "mod_local.h"
|
#include "mod_local.h"
|
||||||
|
#include "atlas.h"
|
||||||
|
|
||||||
#define TURBSCALE ( 256.0f / ( M_PI2 ))
|
#define TURBSCALE ( 256.0f / ( M_PI2 ))
|
||||||
|
|
||||||
@@ -27,8 +28,7 @@ static const float r_turbsin[] =
|
|||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
int allocated[BLOCK_SIZE_MAX];
|
atlas_t atlas;
|
||||||
int max_height; // maximum height currently in use in the block
|
|
||||||
int current_lightmap_texture;
|
int current_lightmap_texture;
|
||||||
msurface_t *dynamic_surfaces;
|
msurface_t *dynamic_surfaces;
|
||||||
msurface_t *lightmap_surfaces[MAX_LIGHTMAPS];
|
msurface_t *lightmap_surfaces[MAX_LIGHTMAPS];
|
||||||
@@ -646,60 +646,17 @@ static void R_AddDynamicLights( const msurface_t *surf )
|
|||||||
*/
|
*/
|
||||||
static void LM_InitBlock( void )
|
static void LM_InitBlock( void )
|
||||||
{
|
{
|
||||||
memset( gl_lms.allocated, 0, sizeof( gl_lms.allocated ));
|
Atlas_Init( &gl_lms.atlas, BLOCK_SIZE );
|
||||||
gl_lms.max_height = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
return Atlas_AllocBlock( &gl_lms.atlas, w, h, x, y );
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void LM_UploadDynamicBlock( void )
|
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 )
|
static void LM_UploadBlock( qboolean dynamic )
|
||||||
|
|||||||
Reference in New Issue
Block a user