2020-11-17 11:16:16 +00:00
/* -----------------------------------------------------------------------------
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 .
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* dependencies */
# include "picointernal.h"
2021-06-02 08:49:27 +00:00
# include "lwo2.h"
2020-11-17 11:16:16 +00:00
/* 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 ( ( const 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 ( ( const 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 */
} ;