mirror of
https://github.com/id-Software/GtkRadiant.git
synced 2026-03-20 08:59:32 +01:00
The GtkRadiant sources as originally released under the GPL license.
This commit is contained in:
445
libs/picomodel/pm_lwo.c
Normal file
445
libs/picomodel/pm_lwo.c
Normal file
@@ -0,0 +1,445 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
|
||||
PicoModel Library
|
||||
|
||||
Copyright (c) 2002, Randy Reddig & seaw0lf
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
Neither the names of the copyright holders nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
/* marker */
|
||||
#define PM_LWO_C
|
||||
|
||||
/* dependencies */
|
||||
#include "picointernal.h"
|
||||
#include "lwo/lwo2.h"
|
||||
|
||||
/* uncomment when debugging this module */
|
||||
/*#define DEBUG_PM_LWO*/
|
||||
|
||||
#ifdef DEBUG_PM_LWO
|
||||
#include "time.h"
|
||||
#endif
|
||||
|
||||
/* helper functions */
|
||||
static const char *lwo_lwIDToStr( unsigned int lwID )
|
||||
{
|
||||
static char lwIDStr[5];
|
||||
|
||||
if (!lwID)
|
||||
{
|
||||
return "n/a";
|
||||
}
|
||||
|
||||
lwIDStr[ 0 ] = (char)((lwID) >> 24);
|
||||
lwIDStr[ 1 ] = (char)((lwID) >> 16);
|
||||
lwIDStr[ 2 ] = (char)((lwID) >> 8);
|
||||
lwIDStr[ 3 ] = (char)((lwID));
|
||||
lwIDStr[ 4 ] = '\0';
|
||||
|
||||
return lwIDStr;
|
||||
}
|
||||
|
||||
/*
|
||||
_lwo_canload()
|
||||
validates a LightWave Object model file. btw, i use the
|
||||
preceding underscore cause it's a static func referenced
|
||||
by one structure only.
|
||||
*/
|
||||
static int _lwo_canload( PM_PARAMS_CANLOAD )
|
||||
{
|
||||
picoMemStream_t *s;
|
||||
unsigned int failID = 0;
|
||||
int failpos = -1;
|
||||
int ret;
|
||||
|
||||
/* create a new pico memorystream */
|
||||
s = _pico_new_memstream( (picoByte_t *)buffer, bufSize );
|
||||
if (s == NULL)
|
||||
{
|
||||
return PICO_PMV_ERROR_MEMORY;
|
||||
}
|
||||
|
||||
ret = lwValidateObject( fileName, s, &failID, &failpos );
|
||||
|
||||
_pico_free_memstream( s );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
_lwo_load()
|
||||
loads a LightWave Object model file.
|
||||
*/
|
||||
static picoModel_t *_lwo_load( PM_PARAMS_LOAD )
|
||||
{
|
||||
picoMemStream_t *s;
|
||||
unsigned int failID = 0;
|
||||
int failpos = -1;
|
||||
lwObject *obj;
|
||||
lwSurface *surface;
|
||||
lwLayer *layer;
|
||||
lwPoint *pt;
|
||||
lwPolygon *pol;
|
||||
lwPolVert *v;
|
||||
lwVMapPt *vm;
|
||||
char name[ 256 ];
|
||||
int i, j, k, numverts;
|
||||
|
||||
picoModel_t *picoModel;
|
||||
picoSurface_t *picoSurface;
|
||||
picoShader_t *picoShader;
|
||||
picoVec3_t xyz, normal;
|
||||
picoVec2_t st;
|
||||
picoColor_t color;
|
||||
|
||||
int defaultSTAxis[ 2 ];
|
||||
picoVec2_t defaultXYZtoSTScale;
|
||||
|
||||
picoVertexCombinationHash_t **hashTable;
|
||||
picoVertexCombinationHash_t *vertexCombinationHash;
|
||||
|
||||
#ifdef DEBUG_PM_LWO
|
||||
clock_t load_start, load_finish, convert_start, convert_finish;
|
||||
double load_elapsed, convert_elapsed;
|
||||
|
||||
load_start = clock();
|
||||
#endif
|
||||
|
||||
/* do frame check */
|
||||
if( frameNum < 0 || frameNum >= 1 )
|
||||
{
|
||||
_pico_printf( PICO_ERROR, "Invalid or out-of-range LWO frame specified" );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* create a new pico memorystream */
|
||||
s = _pico_new_memstream( (picoByte_t *)buffer, bufSize );
|
||||
if (s == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
obj = lwGetObject( fileName, s, &failID, &failpos );
|
||||
|
||||
_pico_free_memstream( s );
|
||||
|
||||
if( !obj ) {
|
||||
_pico_printf( PICO_ERROR, "Couldn't load LWO file, failed on ID '%s', position %d", lwo_lwIDToStr( failID ), failpos );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_PM_LWO
|
||||
convert_start = load_finish = clock();
|
||||
load_elapsed = (double)(load_finish - load_start) / CLOCKS_PER_SEC;
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------
|
||||
pico model creation
|
||||
------------------------------------------------- */
|
||||
|
||||
/* create a new pico model */
|
||||
picoModel = PicoNewModel();
|
||||
if (picoModel == NULL)
|
||||
{
|
||||
_pico_printf( PICO_ERROR, "Unable to allocate a new model" );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* do model setup */
|
||||
PicoSetModelFrameNum( picoModel, frameNum );
|
||||
PicoSetModelNumFrames( picoModel, 1 );
|
||||
PicoSetModelName( picoModel, fileName );
|
||||
PicoSetModelFileName( picoModel, fileName );
|
||||
|
||||
/* create all polygons from layer[ 0 ] that belong to this surface */
|
||||
layer = &obj->layer[0];
|
||||
|
||||
/* warn the user that other layers are discarded */
|
||||
if (obj->nlayers > 1)
|
||||
{
|
||||
_pico_printf( PICO_WARNING, "LWO loader discards any geometry data not in Layer 1 (%d layers found)", obj->nlayers );
|
||||
}
|
||||
|
||||
/* initialize dummy normal */
|
||||
normal[ 0 ] = normal[ 1 ] = normal[ 2 ] = 0.f;
|
||||
|
||||
/* setup default st map */
|
||||
st[ 0 ] = st[ 1 ] = 0.f; /* st[0] holds max, st[1] holds max par one */
|
||||
defaultSTAxis[ 0 ] = 0;
|
||||
defaultSTAxis[ 1 ] = 1;
|
||||
for( i = 0; i < 3; i++ )
|
||||
{
|
||||
float min = layer->bbox[ i ];
|
||||
float max = layer->bbox[ i + 3 ];
|
||||
float size = max - min;
|
||||
|
||||
if (size > st[ 0 ])
|
||||
{
|
||||
defaultSTAxis[ 1 ] = defaultSTAxis[ 0 ];
|
||||
defaultSTAxis[ 0 ] = i;
|
||||
|
||||
st[ 1 ] = st[ 0 ];
|
||||
st[ 0 ] = size;
|
||||
}
|
||||
else if (size > st[ 1 ])
|
||||
{
|
||||
defaultSTAxis[ 1 ] = i;
|
||||
st[ 1 ] = size;
|
||||
}
|
||||
}
|
||||
defaultXYZtoSTScale[ 0 ] = 4.f / st[ 0 ];
|
||||
defaultXYZtoSTScale[ 1 ] = 4.f / st[ 1 ];
|
||||
|
||||
/* LWO surfaces become pico surfaces */
|
||||
surface = obj->surf;
|
||||
while (surface)
|
||||
{
|
||||
/* allocate new pico surface */
|
||||
picoSurface = PicoNewSurface( picoModel );
|
||||
if (picoSurface == NULL)
|
||||
{
|
||||
_pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );
|
||||
PicoFreeModel( picoModel );
|
||||
lwFreeObject( obj );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* LWO model surfaces are all triangle meshes */
|
||||
PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );
|
||||
|
||||
/* set surface name */
|
||||
PicoSetSurfaceName( picoSurface, surface->name );
|
||||
|
||||
/* create new pico shader */
|
||||
picoShader = PicoNewShader( picoModel );
|
||||
if (picoShader == NULL)
|
||||
{
|
||||
_pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );
|
||||
PicoFreeModel( picoModel );
|
||||
lwFreeObject( obj );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* detox and set shader name */
|
||||
strncpy( name, surface->name, sizeof(name) );
|
||||
_pico_first_token( name );
|
||||
_pico_setfext( name, "" );
|
||||
_pico_unixify( name );
|
||||
PicoSetShaderName( picoShader, name );
|
||||
|
||||
/* associate current surface with newly created shader */
|
||||
PicoSetSurfaceShader( picoSurface, picoShader );
|
||||
|
||||
/* copy indices and vertex data */
|
||||
numverts = 0;
|
||||
|
||||
hashTable = PicoNewVertexCombinationHashTable();
|
||||
|
||||
if (hashTable == NULL)
|
||||
{
|
||||
_pico_printf( PICO_ERROR, "Unable to allocate hash table" );
|
||||
PicoFreeModel( picoModel );
|
||||
lwFreeObject( obj );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for( i = 0, pol = layer->polygon.pol; i < layer->polygon.count; i++, pol++ )
|
||||
{
|
||||
/* does this polygon belong to this surface? */
|
||||
if (pol->surf != surface)
|
||||
continue;
|
||||
|
||||
/* we only support polygons of the FACE type */
|
||||
if (pol->type != ID_FACE)
|
||||
{
|
||||
_pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it's type != FACE (%s)", lwo_lwIDToStr( pol->type ) );
|
||||
continue;
|
||||
}
|
||||
|
||||
/* NOTE: LWO has support for non-convex polygons, do we want to store them as well? */
|
||||
if (pol->nverts != 3)
|
||||
{
|
||||
_pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it has != 3 verts (%d)", pol->nverts );
|
||||
continue;
|
||||
}
|
||||
|
||||
for( j = 0, v = pol->v; j < 3; j++, v++ )
|
||||
{
|
||||
pt = &layer->point.pt[ v->index ];
|
||||
|
||||
/* setup data */
|
||||
xyz[ 0 ] = pt->pos[ 0 ];
|
||||
xyz[ 1 ] = pt->pos[ 2 ];
|
||||
xyz[ 2 ] = pt->pos[ 1 ];
|
||||
|
||||
/* doom3 lwo data doesn't seem to have smoothing-angle information */
|
||||
#if 0
|
||||
if(surface->smooth <= 0)
|
||||
{
|
||||
/* use face normals */
|
||||
normal[ 0 ] = v->norm[ 0 ];
|
||||
normal[ 1 ] = v->norm[ 2 ];
|
||||
normal[ 2 ] = v->norm[ 1 ];
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* smooth normals later */
|
||||
normal[ 0 ] = 0;
|
||||
normal[ 1 ] = 0;
|
||||
normal[ 2 ] = 0;
|
||||
}
|
||||
|
||||
st[ 0 ] = xyz[ defaultSTAxis[ 0 ] ] * defaultXYZtoSTScale[ 0 ];
|
||||
st[ 1 ] = xyz[ defaultSTAxis[ 1 ] ] * defaultXYZtoSTScale[ 1 ];
|
||||
|
||||
color[ 0 ] = (picoByte_t)(surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);
|
||||
color[ 1 ] = (picoByte_t)(surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);
|
||||
color[ 2 ] = (picoByte_t)(surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);
|
||||
color[ 3 ] = 0xFF;
|
||||
|
||||
/* set from points */
|
||||
for( k = 0, vm = pt->vm; k < pt->nvmaps; k++, vm++ )
|
||||
{
|
||||
if (vm->vmap->type == LWID_('T','X','U','V'))
|
||||
{
|
||||
/* set st coords */
|
||||
st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ];
|
||||
st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ];
|
||||
}
|
||||
else if (vm->vmap->type == LWID_('R','G','B','A'))
|
||||
{
|
||||
/* set rgba */
|
||||
color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);
|
||||
color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);
|
||||
color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);
|
||||
color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
/* override with polygon data */
|
||||
for( k = 0, vm = v->vm; k < v->nvmaps; k++, vm++ )
|
||||
{
|
||||
if (vm->vmap->type == LWID_('T','X','U','V'))
|
||||
{
|
||||
/* set st coords */
|
||||
st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ];
|
||||
st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ];
|
||||
}
|
||||
else if (vm->vmap->type == LWID_('R','G','B','A'))
|
||||
{
|
||||
/* set rgba */
|
||||
color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);
|
||||
color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);
|
||||
color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);
|
||||
color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
/* find vertex in this surface and if we can't find it there create it */
|
||||
vertexCombinationHash = PicoFindVertexCombinationInHashTable( hashTable, xyz, normal, st, color );
|
||||
|
||||
if (vertexCombinationHash)
|
||||
{
|
||||
/* found an existing one */
|
||||
PicoSetSurfaceIndex( picoSurface, (i * 3 + j ), vertexCombinationHash->index );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* it is a new one */
|
||||
vertexCombinationHash = PicoAddVertexCombinationToHashTable( hashTable, xyz, normal, st, color, (picoIndex_t) numverts );
|
||||
|
||||
if (vertexCombinationHash == NULL)
|
||||
{
|
||||
_pico_printf( PICO_ERROR, "Unable to allocate hash bucket entry table" );
|
||||
PicoFreeVertexCombinationHashTable( hashTable );
|
||||
PicoFreeModel( picoModel );
|
||||
lwFreeObject( obj );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* add the vertex to this surface */
|
||||
PicoSetSurfaceXYZ( picoSurface, numverts, xyz );
|
||||
|
||||
/* set dummy normal */
|
||||
PicoSetSurfaceNormal( picoSurface, numverts, normal );
|
||||
|
||||
/* set color */
|
||||
PicoSetSurfaceColor( picoSurface, 0, numverts, color );
|
||||
|
||||
/* set st coords */
|
||||
PicoSetSurfaceST( picoSurface, 0, numverts, st );
|
||||
|
||||
/* set index */
|
||||
PicoSetSurfaceIndex( picoSurface, (i * 3 + j ), (picoIndex_t) numverts );
|
||||
|
||||
numverts++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* free the hashtable */
|
||||
PicoFreeVertexCombinationHashTable( hashTable );
|
||||
|
||||
/* get next surface */
|
||||
surface = surface->next;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_PM_LWO
|
||||
load_start = convert_finish = clock();
|
||||
#endif
|
||||
|
||||
lwFreeObject( obj );
|
||||
|
||||
#ifdef DEBUG_PM_LWO
|
||||
load_finish = clock();
|
||||
load_elapsed += (double)(load_finish - load_start) / CLOCKS_PER_SEC;
|
||||
convert_elapsed = (double)(convert_finish - convert_start) / CLOCKS_PER_SEC;
|
||||
_pico_printf( PICO_NORMAL, "Loaded model in in %-.2f second(s) (loading: %-.2fs converting: %-.2fs)\n", load_elapsed + convert_elapsed, load_elapsed, convert_elapsed );
|
||||
#endif
|
||||
|
||||
/* return the new pico model */
|
||||
return picoModel;
|
||||
}
|
||||
|
||||
/* pico file format module definition */
|
||||
const picoModule_t picoModuleLWO =
|
||||
{
|
||||
"1.0", /* module version string */
|
||||
"LightWave Object", /* module display name */
|
||||
"Arnout van Meer", /* author's name */
|
||||
"2003 Arnout van Meer, 2000 Ernie Wright", /* module copyright */
|
||||
{
|
||||
"lwo", NULL, NULL, NULL /* default extensions to use */
|
||||
},
|
||||
_lwo_canload, /* validation routine */
|
||||
_lwo_load, /* load routine */
|
||||
NULL, /* save validation routine */
|
||||
NULL /* save routine */
|
||||
};
|
||||
Reference in New Issue
Block a user