Ported picomodel.c from 1.5

Improvements over 1.4/1.5 are in.
This commit is contained in:
Christophe Mateos 2016-12-04 01:43:28 +01:00
parent 3fb0a994e2
commit 41f9d3a1e1
4 changed files with 593 additions and 222 deletions

View file

@ -105,6 +105,7 @@ struct picoSurface_s
int numVertexes, maxVertexes; int numVertexes, maxVertexes;
picoVec3_t *xyz; picoVec3_t *xyz;
picoVec3_t *normal; picoVec3_t *normal;
picoIndex_t *smoothingGroup;
int numSTArrays, maxSTArrays; int numSTArrays, maxSTArrays;
picoVec2_t **st; picoVec2_t **st;
@ -216,6 +217,9 @@ const picoModule_t **PicoModuleList( int *numModules );
picoModel_t *PicoLoadModel( char *name, int frameNum ); picoModel_t *PicoLoadModel( char *name, int frameNum );
typedef size_t(*PicoInputStreamReadFunc)(void* inputStream, unsigned char* buffer, size_t length);
picoModel_t* PicoModuleLoadModelStream(const picoModule_t* module, void* inputStream, PicoInputStreamReadFunc inputStreamRead, size_t streamLength, int frameNum);
/* model functions */ /* model functions */
picoModel_t *PicoNewModel( void ); picoModel_t *PicoNewModel( void );

View file

@ -161,35 +161,22 @@ void *_pico_realloc( void **ptr, size_t oldSize, size_t newSize ){
* as custom clone size (the string is cropped to fit into mem * as custom clone size (the string is cropped to fit into mem
* if needed). -sea * if needed). -sea
*/ */
char *_pico_clone_alloc( char *str, int size ){ char *_pico_clone_alloc(const char *str)
{
char* cloned; char* cloned;
size_t cloneSize;
/* sanity check */ /* sanity check */
if ( str == NULL ) { if (str == NULL)
return NULL; return NULL;
}
/* set real size of cloned string */
cloneSize = ( size < 0 ) ? strlen( str ) : size;
/* allocate memory */ /* allocate memory */
cloned = _pico_alloc( cloneSize + 1 ); /* bugfix! */ cloned = _pico_alloc(strlen(str) + 1);
if ( cloned == NULL ) { if (cloned == NULL)
return NULL; return NULL;
}
/* zero out memory allocated by cloned string */
memset( cloned,0,cloneSize );
/* copy input string to cloned string */ /* copy input string to cloned string */
if ( cloneSize < strlen( str ) ) {
memcpy( cloned,str,cloneSize );
cloned[ cloneSize ] = '\0';
}
else {
strcpy(cloned, str); strcpy(cloned, str);
}
/* return ptr to cloned string */ /* return ptr to cloned string */
return cloned; return cloned;
} }

View file

@ -116,7 +116,7 @@ extern void ( *_pico_ptr_print )( int, const char* );
void *_pico_alloc( size_t size ); void *_pico_alloc( size_t size );
void *_pico_calloc( size_t num, size_t size ); void *_pico_calloc( size_t num, size_t size );
void *_pico_realloc( void **ptr, size_t oldSize, size_t newSize ); void *_pico_realloc( void **ptr, size_t oldSize, size_t newSize );
char *_pico_clone_alloc( char *str, int size ); char *_pico_clone_alloc( char *str/*, int size*/ );
void _pico_free( void *ptr ); void _pico_free( void *ptr );
/* files */ /* files */

View file

@ -143,7 +143,46 @@ void PicoSetPrintFunc( void ( *func )( int, const char* ) ){
} }
} }
picoModel_t *PicoModuleLoadModel( const picoModule_t* pm, char* fileName, picoByte_t* buffer, int bufSize, int frameNum ){
char *modelFileName, *remapFileName;
/* see whether this module can load the model file or not */
if ( pm->canload( fileName, buffer, bufSize ) == PICO_PMV_OK ) {
/* use loader provided by module to read the model data */
picoModel_t* model = pm->load( fileName, frameNum, buffer, bufSize );
if ( model == NULL ) {
_pico_free_file( buffer );
return NULL;
}
/* assign pointer to file format module */
model->module = pm;
/* get model file name */
modelFileName = PicoGetModelFileName( model );
/* apply model remappings from <model>.remap */
if ( strlen( modelFileName ) ) {
/* alloc copy of model file name */
remapFileName = _pico_alloc( strlen( modelFileName ) + 20 );
if ( remapFileName != NULL ) {
/* copy model file name and change extension */
strcpy( remapFileName, modelFileName );
_pico_setfext( remapFileName, "remap" );
/* try to remap model; we don't handle the result */
PicoRemapModel( model, remapFileName );
/* free the remap file name string */
_pico_free( remapFileName );
}
}
return model;
}
return NULL;
}
/* /*
PicoLoadModel() PicoLoadModel()
@ -155,7 +194,6 @@ picoModel_t *PicoLoadModel( char *fileName, int frameNum ){
picoModel_t *model; picoModel_t *model;
picoByte_t *buffer; picoByte_t *buffer;
int bufSize; int bufSize;
char *modelFileName, *remapFileName;
/* init */ /* init */
@ -194,38 +232,8 @@ picoModel_t *PicoLoadModel( char *fileName, int frameNum ){
continue; continue;
} }
/* see whether this module can load the model file or not */ model = PicoModuleLoadModel( pm, fileName, buffer, bufSize, frameNum );
if ( pm->canload( fileName, buffer, bufSize ) == PICO_PMV_OK ) { if ( model != NULL ) {
/* use loader provided by module to read the model data */
model = pm->load( fileName, frameNum, buffer, bufSize );
if ( model == NULL ) {
_pico_free_file( buffer );
return NULL;
}
/* assign pointer to file format module */
model->module = pm;
/* get model file name */
modelFileName = PicoGetModelFileName( model );
/* apply model remappings from <model>.remap */
if ( strlen( modelFileName ) ) {
/* alloc copy of model file name */
remapFileName = _pico_alloc( strlen( modelFileName ) + 20 );
if ( remapFileName != NULL ) {
/* copy model file name and change extension */
strcpy( remapFileName, modelFileName );
_pico_setfext( remapFileName, "remap" );
/* try to remap model; we don't handle the result */
PicoRemapModel( model, remapFileName );
/* free the remap file name string */
_pico_free( remapFileName );
}
}
/* model was loaded, so break out of loop */ /* model was loaded, so break out of loop */
break; break;
} }
@ -236,7 +244,53 @@ picoModel_t *PicoLoadModel( char *fileName, int frameNum ){
_pico_free_file( buffer ); _pico_free_file( buffer );
} }
/* return */ return model;
}
/*
FIXME: From 1.5; Unused yet
*/
picoModel_t *PicoModuleLoadModelStream( const picoModule_t* module, void* inputStream, PicoInputStreamReadFunc inputStreamRead, size_t streamLength, int frameNum )
{
picoModel_t *model;
picoByte_t *buffer;
int bufSize;
/* init */
model = NULL;
if ( inputStream == NULL )
{
_pico_printf( PICO_ERROR, "PicoLoadModel: invalid input stream (inputStream == NULL)" );
return NULL;
}
if ( inputStreamRead == NULL )
{
_pico_printf( PICO_ERROR, "PicoLoadModel: invalid input stream (inputStreamRead == NULL) ");
return NULL;
}
buffer = _pico_alloc( streamLength + 1 );
bufSize = (int)inputStreamRead( inputStream, buffer, streamLength );
buffer[ bufSize ] = '\0';
{
// dummy filename
char fileName[128];
fileName[0] = '.';
strncpy( fileName + 1, module->defaultExts[ 0 ], 126 );
fileName[127] = '\0';
model = PicoModuleLoadModel( module, fileName, buffer, bufSize, frameNum );
}
/* free memory used by file buffer */
if ( model != 0 ) {
_pico_free( buffer );
}
return model; return model;
} }
@ -283,7 +337,6 @@ picoModel_t *PicoNewModel( void ){
void PicoFreeModel( picoModel_t *model ){ void PicoFreeModel( picoModel_t *model ){
int i; int i;
/* sanity check */ /* sanity check */
if ( model == NULL ) { if ( model == NULL ) {
return; return;
@ -294,6 +347,10 @@ void PicoFreeModel( picoModel_t *model ){
_pico_free( model->name ); _pico_free( model->name );
} }
if ( model->fileName ) {
_pico_free( model->fileName );
}
/* free shaders */ /* free shaders */
for ( i = 0; i < model->numShaders; i++ ) for ( i = 0; i < model->numShaders; i++ )
PicoFreeShader( model->shader[ i ] ); PicoFreeShader( model->shader[ i ] );
@ -444,7 +501,6 @@ void PicoFreeShader( picoShader_t *shader ){
picoShader_t *PicoFindShader( picoModel_t *model, char *name, int caseSensitive ){ picoShader_t *PicoFindShader( picoModel_t *model, char *name, int caseSensitive ){
int i; int i;
/* sanity checks */ /* sanity checks */
if ( model == NULL || name == NULL ) { /* sea: null name fix */ if ( model == NULL || name == NULL ) { /* sea: null name fix */
return NULL; return NULL;
@ -535,9 +591,14 @@ void PicoFreeSurface( picoSurface_t *surface ){
/* free bits */ /* free bits */
_pico_free( surface->xyz ); _pico_free( surface->xyz );
_pico_free( surface->normal ); _pico_free( surface->normal );
_pico_free( surface->smoothingGroup );
_pico_free( surface->index ); _pico_free( surface->index );
_pico_free( surface->faceNormal ); _pico_free( surface->faceNormal );
if ( surface->name ) {
_pico_free( surface->name );
}
/* free arrays */ /* free arrays */
for ( i = 0; i < surface->numSTArrays; i++ ) for ( i = 0; i < surface->numSTArrays; i++ )
_pico_free( surface->st[ i ] ); _pico_free( surface->st[ i ] );
@ -591,6 +652,9 @@ int PicoAdjustSurface( picoSurface_t *surface, int numVertexes, int numSTArrays,
if ( !_pico_realloc( (void *) &surface->normal, surface->numVertexes * sizeof( *surface->normal ), surface->maxVertexes * sizeof( *surface->normal ) ) ) { if ( !_pico_realloc( (void *) &surface->normal, surface->numVertexes * sizeof( *surface->normal ), surface->maxVertexes * sizeof( *surface->normal ) ) ) {
return 0; return 0;
} }
if ( !_pico_realloc( (void *) &surface->smoothingGroup, surface->numVertexes * sizeof( *surface->smoothingGroup ), surface->maxVertexes * sizeof( *surface->smoothingGroup ) ) ) {
return 0;
}
for ( i = 0; i < surface->numSTArrays; i++ ) for ( i = 0; i < surface->numSTArrays; i++ )
if ( !_pico_realloc( (void*) &surface->st[ i ], surface->numVertexes * sizeof( *surface->st[ i ] ), surface->maxVertexes * sizeof( *surface->st[ i ] ) ) ) { if ( !_pico_realloc( (void*) &surface->st[ i ], surface->numVertexes * sizeof( *surface->st[ i ] ), surface->maxVertexes * sizeof( *surface->st[ i ] ) ) ) {
return 0; return 0;
@ -720,7 +784,7 @@ void PicoSetModelName( picoModel_t *model, char *name ){
_pico_free( model->name ); _pico_free( model->name );
} }
model->name = _pico_clone_alloc( name,-1 ); model->name = _pico_clone_alloc( name/*,-1*/ );
} }
@ -733,7 +797,7 @@ void PicoSetModelFileName( picoModel_t *model, char *fileName ){
_pico_free( model->fileName ); _pico_free( model->fileName );
} }
model->fileName = _pico_clone_alloc( fileName,-1 ); model->fileName = _pico_clone_alloc( fileName/*,-1*/ );
} }
@ -773,7 +837,7 @@ void PicoSetShaderName( picoShader_t *shader, char *name ){
_pico_free( shader->name ); _pico_free( shader->name );
} }
shader->name = _pico_clone_alloc( name,-1 ); shader->name = _pico_clone_alloc( name/*,-1*/ );
} }
@ -786,7 +850,7 @@ void PicoSetShaderMapName( picoShader_t *shader, char *mapName ){
_pico_free( shader->mapName ); _pico_free( shader->mapName );
} }
shader->mapName = _pico_clone_alloc( mapName,-1 ); shader->mapName = _pico_clone_alloc( mapName/*,-1*/ );
} }
@ -887,7 +951,7 @@ void PicoSetSurfaceName( picoSurface_t *surface, char *name ){
_pico_free( surface->name ); _pico_free( surface->name );
} }
surface->name = _pico_clone_alloc( name,-1 ); surface->name = _pico_clone_alloc( name/*,-1*/ );
} }
@ -990,6 +1054,15 @@ void PicoSetFaceNormal( picoSurface_t *surface, int num, picoVec3_t normal ){
_pico_copy_vec( normal, surface->faceNormal[ num ] ); _pico_copy_vec( normal, surface->faceNormal[ num ] );
} }
void PicoSetSurfaceSmoothingGroup( picoSurface_t *surface, int num, picoIndex_t smoothingGroup ){
if ( num < 0 ) {
return;
}
if ( !PicoAdjustSurface( surface, num + 1, 0, 0, 0, 0 ) ) {
return;
}
surface->smoothingGroup[ num ] = smoothingGroup;
}
void PicoSetSurfaceSpecial( picoSurface_t *surface, int num, int special ){ void PicoSetSurfaceSpecial( picoSurface_t *surface, int num, int special ){
if ( surface == NULL || num < 0 || num >= PICO_MAX_SPECIAL ) { if ( surface == NULL || num < 0 || num >= PICO_MAX_SPECIAL ) {
@ -1335,6 +1408,12 @@ picoVec_t *PicoGetFaceNormal( picoSurface_t *surface, int num ){
return surface->faceNormal[ num ]; return surface->faceNormal[ num ];
} }
picoIndex_t PicoGetSurfaceSmoothingGroup( picoSurface_t *surface, int num ){
if ( surface == NULL || num < 0 || num > surface->numVertexes ) {
return -1;
}
return surface->smoothingGroup[ num ];
}
int PicoGetSurfaceSpecial( picoSurface_t *surface, int num ){ int PicoGetSurfaceSpecial( picoSurface_t *surface, int num ){
if ( surface == NULL || num < 0 || num >= PICO_MAX_SPECIAL ) { if ( surface == NULL || num < 0 || num >= PICO_MAX_SPECIAL ) {
@ -1535,7 +1614,7 @@ picoVertexCombinationHash_t *PicoAddVertexCombinationToHashTable( picoVertexComb
fixme: needs non-naive algorithm fixme: needs non-naive algorithm
*/ */
int PicoFindSurfaceVertexNum( picoSurface_t *surface, picoVec3_t xyz, picoVec3_t normal, int numSTs, picoVec2_t *st, int numColors, picoColor_t *color ){ int PicoFindSurfaceVertexNum( picoSurface_t *surface, picoVec3_t xyz, picoVec3_t normal, int numSTs, picoVec2_t *st, int numColors, picoColor_t *color, picoIndex_t smoothingGroup ){
int i, j; int i, j;
@ -1557,6 +1636,11 @@ int PicoFindSurfaceVertexNum( picoSurface_t *surface, picoVec3_t xyz, picoVec3_t
continue; continue;
} }
/* check normal */
if ( surface->smoothingGroup[ i ] != smoothingGroup ) {
continue;
}
/* check st */ /* check st */
if ( numSTs > 0 && st != NULL ) { if ( numSTs > 0 && st != NULL ) {
for ( j = 0; j < numSTs; j++ ) for ( j = 0; j < numSTs; j++ )
@ -1598,126 +1682,420 @@ int PicoFindSurfaceVertexNum( picoSurface_t *surface, picoVec3_t xyz, picoVec3_t
fixes broken normals (certain formats bork normals) fixes broken normals (certain formats bork normals)
*/ */
#define MAX_NORMAL_VOTES 128 //#define MAX_NORMAL_VOTES 128
#define EQUAL_NORMAL_EPSILON 0.01 //#define EQUAL_NORMAL_EPSILON 0.01
#define BAD_NORMAL_EPSILON 0.5 //#define BAD_NORMAL_EPSILON 0.5
//
//void PicoFixSurfaceNormals( picoSurface_t *surface ){
// int i, j, k, a, b, c, numVotes, faceIndex;
// picoVec3_t votes[ MAX_NORMAL_VOTES ];
// picoVec3_t *normals, diff;
// picoVec4_t plane;
//
//
// /* dummy check */
// if ( surface == NULL || surface->numVertexes == 0 ) {
// return;
// }
//
// /* fixme: handle other surface types */
// if ( surface->type != PICO_TRIANGLES ) {
// return;
// }
//
// /* allocate normal storage */
// normals = _pico_alloc( surface->numVertexes * sizeof( *normals ) );
// if ( normals == NULL ) {
// _pico_printf( PICO_ERROR, "PicoFixSurfaceNormals: Unable to allocate memory for temporary normal storage" );
// return;
// }
//
// /* zero it out */
// memset( normals, 0, surface->numVertexes * sizeof( *normals ) );
//
// /* walk vertex list */
// for ( i = 0; i < surface->numVertexes; i++ )
// {
// /* zero out votes */
// numVotes = 0;
//
// /* find all the triangles that reference this vertex */
// for ( j = 0, faceIndex = 0; j < surface->numIndexes; j += 3, faceIndex++ )
// {
// /* get triangle */
// a = surface->index[ j ];
// b = surface->index[ j + 1 ];
// c = surface->index[ j + 2 ];
//
// /* ignore degenerate triangles */
// if ( a == b || b == c || c == a ) {
// continue;
// }
//
// /* ignore indexes out of range */
// if ( a < 0 || a >= surface->numVertexes ||
// b < 0 || b >= surface->numVertexes ||
// c < 0 || c >= surface->numVertexes ) {
// continue;
// }
//
// /* test triangle */
// if ( a == i || b == i || c == i ) {
// /* if this surface has face normals */
// if ( surface->numFaceNormals && faceIndex < surface->numFaceNormals ) {
// _pico_copy_vec( surface->faceNormal[ faceIndex ], plane );
// if ( plane[ 0 ] == 0.f && plane[ 1 ] == 0.f && plane[ 2 ] == 0.f ) {
// /* if null normal, make plane from the 3 points */
// if ( _pico_calc_plane( plane, surface->xyz[ a ], surface->xyz[ b ], surface->xyz[ c ] ) == 0 ) {
// continue;
// }
// }
// }
// /* make a plane from the 3 points */
// else if ( _pico_calc_plane( plane, surface->xyz[ a ], surface->xyz[ b ], surface->xyz[ c ] ) == 0 ) {
// continue;
// }
//
// /* see if this normal has already been voted */
// for ( k = 0; k < numVotes; k++ )
// {
// _pico_subtract_vec( plane, votes[ k ], diff );
// if ( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
// fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
// fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON ) {
// break;
// }
// }
//
// /* add a new vote? */
// if ( k == numVotes && numVotes < MAX_NORMAL_VOTES ) {
// _pico_copy_vec( plane, votes[ numVotes ] );
// numVotes++;
// }
// }
// }
//
// /* tally votes */
// if ( numVotes > 0 ) {
// /* create average normal */
// _pico_zero_vec( normals[ i ] );
// for ( k = 0; k < numVotes; k++ )
// _pico_add_vec( normals[ i ], votes[ k ], normals[ i ] );
//
// /* normalize it */
// if ( _pico_normalize_vec( normals[ i ] ) ) {
// /* test against actual normal */
// if ( fabs( _pico_dot_vec( normals[ i ], surface->normal[ i ] ) - 1 ) > BAD_NORMAL_EPSILON ) {
// //% printf( "Normal %8d: (%f %f %f) -> (%f %f %f)\n", i,
// //% surface->normal[ i ][ 0 ], surface->normal[ i ][ 1 ], surface->normal[ i ][ 2 ],
// //% normals[ i ][ 0 ], normals[ i ][ 1 ], normals[ i ][ 2 ] );
// _pico_copy_vec( normals[ i ], surface->normal[ i ] );
// }
// }
// }
// }
//
// /* free normal storage */
// _pico_free( normals );
//}
typedef struct _IndexArray IndexArray;
struct _IndexArray
{
picoIndex_t* data;
picoIndex_t* last;
};
void indexarray_push_back(IndexArray* self, picoIndex_t value) {
*self->last++ = value;
}
size_t indexarray_size(IndexArray* self) {
return self->last - self->data;
}
void indexarray_reserve(IndexArray* self, size_t size) {
self->data = self->last = _pico_calloc(size, sizeof(picoIndex_t));
}
void indexarray_clear(IndexArray* self) {
_pico_free(self->data);
}
typedef struct _BinaryTreeNode BinaryTreeNode;
struct _BinaryTreeNode
{
picoIndex_t left;
picoIndex_t right;
};
typedef struct _BinaryTree BinaryTree;
struct _BinaryTree
{
BinaryTreeNode* data;
BinaryTreeNode* last;
};
void binarytree_extend(BinaryTree* self) {
self->last->left = 0;
self->last->right = 0;
++self->last;
}
size_t binarytree_size(BinaryTree* self) {
return self->last - self->data;
}
void binarytree_reserve(BinaryTree* self, size_t size) {
self->data = self->last = _pico_calloc(size, sizeof(BinaryTreeNode));
}
void binarytree_clear(BinaryTree* self) {
_pico_free(self->data);
}
typedef int(*LessFunc)(void*, picoIndex_t, picoIndex_t);
typedef struct _UniqueIndices UniqueIndices;
struct _UniqueIndices
{
BinaryTree tree;
IndexArray indices;
LessFunc lessFunc;
void* lessData;
};
size_t UniqueIndices_size(UniqueIndices* self) {
return binarytree_size(&self->tree);
}
void UniqueIndices_reserve(UniqueIndices* self, size_t size) {
binarytree_reserve(&self->tree, size);
indexarray_reserve(&self->indices, size);
}
void UniqueIndices_init(UniqueIndices* self, LessFunc lessFunc, void* lessData) {
self->lessFunc = lessFunc;
self->lessData = lessData;
}
void UniqueIndices_destroy(UniqueIndices* self) {
binarytree_clear(&self->tree);
indexarray_clear(&self->indices);
}
picoIndex_t UniqueIndices_find_or_insert(UniqueIndices* self, picoIndex_t value) {
picoIndex_t index = 0;
for (;; )
{
if (self->lessFunc(self->lessData, value, self->indices.data[index])) {
BinaryTreeNode* node = self->tree.data + index;
if (node->left != 0) {
index = node->left;
continue;
}
else
{
node->left = (picoIndex_t)binarytree_size(&self->tree);
binarytree_extend(&self->tree);
indexarray_push_back(&self->indices, value);
return node->left;
}
}
if (self->lessFunc(self->lessData, self->indices.data[index], value)) {
BinaryTreeNode* node = self->tree.data + index;
if (node->right != 0) {
index = node->right;
continue;
}
else
{
node->right = (picoIndex_t)binarytree_size(&self->tree);
binarytree_extend(&self->tree);
indexarray_push_back(&self->indices, value);
return node->right;
}
}
return index;
}
}
picoIndex_t UniqueIndices_insert(UniqueIndices* self, picoIndex_t value) {
if (self->tree.data == self->tree.last) {
binarytree_extend(&self->tree);
indexarray_push_back(&self->indices, value);
return 0;
}
else
{
return UniqueIndices_find_or_insert(self, value);
}
}
typedef struct picoSmoothVertices_s picoSmoothVertices_t;
struct picoSmoothVertices_s
{
picoVec3_t* xyz;
picoIndex_t* smoothingGroups;
};
int lessSmoothVertex(void* data, picoIndex_t first, picoIndex_t second) {
picoSmoothVertices_t* smoothVertices = data;
if (smoothVertices->xyz[first][0] != smoothVertices->xyz[second][0]) {
return smoothVertices->xyz[first][0] < smoothVertices->xyz[second][0];
}
if (smoothVertices->xyz[first][1] != smoothVertices->xyz[second][1]) {
return smoothVertices->xyz[first][1] < smoothVertices->xyz[second][1];
}
if (smoothVertices->xyz[first][2] != smoothVertices->xyz[second][2]) {
return smoothVertices->xyz[first][2] < smoothVertices->xyz[second][2];
}
if (smoothVertices->smoothingGroups[first] != smoothVertices->smoothingGroups[second]) {
return smoothVertices->smoothingGroups[first] < smoothVertices->smoothingGroups[second];
}
return 0;
}
void _pico_vertices_combine_shared_normals(picoVec3_t* xyz, picoIndex_t* smoothingGroups, picoVec3_t* normals, picoIndex_t numVertices) {
UniqueIndices vertices;
IndexArray indices;
picoSmoothVertices_t smoothVertices = { xyz, smoothingGroups };
UniqueIndices_init(&vertices, lessSmoothVertex, &smoothVertices);
UniqueIndices_reserve(&vertices, numVertices);
indexarray_reserve(&indices, numVertices);
{
picoIndex_t i = 0;
for (; i < numVertices; ++i)
{
size_t size = UniqueIndices_size(&vertices);
picoIndex_t index = UniqueIndices_insert(&vertices, i);
if ((size_t)index != size) {
float* normal = normals[vertices.indices.data[index]];
_pico_add_vec(normal, normals[i], normal);
}
indexarray_push_back(&indices, index);
}
}
{
picoIndex_t maxIndex = 0;
picoIndex_t* i = indices.data;
for (; i != indices.last; ++i)
{
if (*i <= maxIndex) {
_pico_copy_vec(normals[vertices.indices.data[*i]], normals[i - indices.data]);
}
else
{
maxIndex = *i;
}
}
}
UniqueIndices_destroy(&vertices);
indexarray_clear(&indices);
}
typedef picoVec3_t* picoNormalIter_t;
typedef picoIndex_t* picoIndexIter_t;
#define THE_CROSSPRODUCTS_OF_ANY_PAIR_OF_EDGES_OF_A_GIVEN_TRIANGLE_ARE_EQUAL 1
void _pico_triangles_generate_weighted_normals(picoIndexIter_t first, picoIndexIter_t end, picoVec3_t* xyz, picoVec3_t* normals) {
for (; first != end; first += 3)
{
#if (THE_CROSSPRODUCTS_OF_ANY_PAIR_OF_EDGES_OF_A_GIVEN_TRIANGLE_ARE_EQUAL)
picoVec3_t weightedNormal;
{
float* a = xyz[*(first + 0)];
float* b = xyz[*(first + 1)];
float* c = xyz[*(first + 2)];
picoVec3_t ba, ca;
_pico_subtract_vec(b, a, ba);
_pico_subtract_vec(c, a, ca);
_pico_cross_vec(ca, ba, weightedNormal);
}
#endif
{
int j = 0;
for (; j < 3; ++j)
{
float* normal = normals[*(first + j)];
#if ( !THE_CROSSPRODUCTS_OF_ANY_PAIR_OF_EDGES_OF_A_GIVEN_TRIANGLE_ARE_EQUAL )
picoVec3_t weightedNormal;
{
float* a = xyz[*(first + ((j + 0) % 3))];
float* b = xyz[*(first + ((j + 1) % 3))];
float* c = xyz[*(first + ((j + 2) % 3))];
picoVec3_t ba, ca;
_pico_subtract_vec(b, a, ba);
_pico_subtract_vec(c, a, ca);
_pico_cross_vec(ca, ba, weightedNormal);
}
#endif
_pico_add_vec(weightedNormal, normal, normal);
}
}
}
}
void _pico_normals_zero(picoNormalIter_t first, picoNormalIter_t last) {
for (; first != last; ++first)
{
_pico_zero_vec(*first);
}
}
void _pico_normals_normalize(picoNormalIter_t first, picoNormalIter_t last) {
for (; first != last; ++first)
{
_pico_normalize_vec(*first);
}
}
double _pico_length_vec(picoVec3_t vec) {
return sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]);
}
#define NORMAL_UNIT_LENGTH_EPSILON 0.01
#define FLOAT_EQUAL_EPSILON( f, other, epsilon ) ( fabs( f - other ) < epsilon )
int _pico_normal_is_unit_length(picoVec3_t normal) {
return FLOAT_EQUAL_EPSILON(_pico_length_vec(normal), 1.0, NORMAL_UNIT_LENGTH_EPSILON);
}
int _pico_normal_within_tolerance(picoVec3_t normal, picoVec3_t other) {
return _pico_dot_vec(normal, other) > 0.0f;
}
void _pico_normals_assign_generated_normals(picoNormalIter_t first, picoNormalIter_t last, picoNormalIter_t generated) {
for (; first != last; ++first, ++generated)
{
if (!_pico_normal_is_unit_length(*first) || !_pico_normal_within_tolerance(*first, *generated)) {
_pico_copy_vec(*generated, *first);
}
}
}
void PicoFixSurfaceNormals(picoSurface_t* surface) { void PicoFixSurfaceNormals(picoSurface_t* surface) {
int i, j, k, a, b, c, numVotes, faceIndex; picoVec3_t* normals = (picoVec3_t*)_pico_calloc(surface->numVertexes, sizeof(picoVec3_t));
picoVec3_t votes[ MAX_NORMAL_VOTES ];
picoVec3_t *normals, diff;
picoVec4_t plane;
_pico_normals_zero(normals, normals + surface->numVertexes);
/* dummy check */ _pico_triangles_generate_weighted_normals(surface->index, surface->index + surface->numIndexes, surface->xyz, normals);
if ( surface == NULL || surface->numVertexes == 0 ) { _pico_vertices_combine_shared_normals(surface->xyz, surface->smoothingGroup, normals, surface->numVertexes);
return;
}
/* fixme: handle other surface types */ _pico_normals_normalize(normals, normals + surface->numVertexes);
if ( surface->type != PICO_TRIANGLES ) {
return;
}
/* allocate normal storage */ _pico_normals_assign_generated_normals(surface->normal, surface->normal + surface->numVertexes, normals);
normals = _pico_alloc( surface->numVertexes * sizeof( *normals ) );
if ( normals == NULL ) {
_pico_printf( PICO_ERROR, "PicoFixSurfaceNormals: Unable to allocate memory for temporary normal storage" );
return;
}
/* zero it out */
memset( normals, 0, surface->numVertexes * sizeof( *normals ) );
/* walk vertex list */
for ( i = 0; i < surface->numVertexes; i++ )
{
/* zero out votes */
numVotes = 0;
/* find all the triangles that reference this vertex */
for ( j = 0, faceIndex = 0; j < surface->numIndexes; j += 3, faceIndex++ )
{
/* get triangle */
a = surface->index[ j ];
b = surface->index[ j + 1 ];
c = surface->index[ j + 2 ];
/* ignore degenerate triangles */
if ( a == b || b == c || c == a ) {
continue;
}
/* ignore indexes out of range */
if ( a < 0 || a >= surface->numVertexes ||
b < 0 || b >= surface->numVertexes ||
c < 0 || c >= surface->numVertexes ) {
continue;
}
/* test triangle */
if ( a == i || b == i || c == i ) {
/* if this surface has face normals */
if ( surface->numFaceNormals && faceIndex < surface->numFaceNormals ) {
_pico_copy_vec( surface->faceNormal[ faceIndex ], plane );
if ( plane[ 0 ] == 0.f && plane[ 1 ] == 0.f && plane[ 2 ] == 0.f ) {
/* if null normal, make plane from the 3 points */
if ( _pico_calc_plane( plane, surface->xyz[ a ], surface->xyz[ b ], surface->xyz[ c ] ) == 0 ) {
continue;
}
}
}
/* make a plane from the 3 points */
else if ( _pico_calc_plane( plane, surface->xyz[ a ], surface->xyz[ b ], surface->xyz[ c ] ) == 0 ) {
continue;
}
/* see if this normal has already been voted */
for ( k = 0; k < numVotes; k++ )
{
_pico_subtract_vec( plane, votes[ k ], diff );
if ( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON ) {
break;
}
}
/* add a new vote? */
if ( k == numVotes && numVotes < MAX_NORMAL_VOTES ) {
_pico_copy_vec( plane, votes[ numVotes ] );
numVotes++;
}
}
}
/* tally votes */
if ( numVotes > 0 ) {
/* create average normal */
_pico_zero_vec( normals[ i ] );
for ( k = 0; k < numVotes; k++ )
_pico_add_vec( normals[ i ], votes[ k ], normals[ i ] );
/* normalize it */
if ( _pico_normalize_vec( normals[ i ] ) ) {
/* test against actual normal */
if ( fabs( _pico_dot_vec( normals[ i ], surface->normal[ i ] ) - 1 ) > BAD_NORMAL_EPSILON ) {
//% printf( "Normal %8d: (%f %f %f) -> (%f %f %f)\n", i,
//% surface->normal[ i ][ 0 ], surface->normal[ i ][ 1 ], surface->normal[ i ][ 2 ],
//% normals[ i ][ 0 ], normals[ i ][ 1 ], normals[ i ][ 2 ] );
_pico_copy_vec( normals[ i ], surface->normal[ i ] );
}
}
}
}
/* free normal storage */
_pico_free(normals); _pico_free(normals);
} }
/* /*
PicoRemapModel() - sea PicoRemapModel() - sea
remaps model material/etc. information using the remappings remaps model material/etc. information using the remappings
@ -1799,7 +2177,7 @@ int PicoRemapModel( picoModel_t *model, char *remapFile ){
if ( !strlen( p->token ) ) { if ( !strlen( p->token ) ) {
continue; continue;
} }
materialName = _pico_clone_alloc( p->token,-1 ); materialName = _pico_clone_alloc( p->token/*,-1*/ );
if ( materialName == NULL ) { if ( materialName == NULL ) {
_prm_error_return; _prm_error_return;
} }
@ -1857,7 +2235,7 @@ int PicoRemapModel( picoModel_t *model, char *remapFile ){
} }
/* temporary copy of material name */ /* temporary copy of material name */
tempMaterialName = _pico_clone_alloc( p->token,-1 ); tempMaterialName = _pico_clone_alloc( p->token/*,-1*/ );
if ( tempMaterialName == NULL ) { if ( tempMaterialName == NULL ) {
_prm_error_return; _prm_error_return;
} }
@ -2002,7 +2380,7 @@ int PicoRemapModel( picoModel_t *model, char *remapFile ){
void PicoAddTriangleToModel( picoModel_t *model, picoVec3_t** xyz, picoVec3_t** normals, void PicoAddTriangleToModel( picoModel_t *model, picoVec3_t** xyz, picoVec3_t** normals,
int numSTs, picoVec2_t **st, int numColors, picoColor_t **colors, int numSTs, picoVec2_t **st, int numColors, picoColor_t **colors,
picoShader_t* shader ){ picoShader_t* shader, picoIndex_t* smoothingGroup ){
int i, j; int i, j;
int vertDataIndex; int vertDataIndex;
picoSurface_t* workSurface = NULL; picoSurface_t* workSurface = NULL;
@ -2038,7 +2416,7 @@ void PicoAddTriangleToModel( picoModel_t *model, picoVec3_t** xyz, picoVec3_t**
int newVertIndex = PicoGetSurfaceNumIndexes( workSurface ); int newVertIndex = PicoGetSurfaceNumIndexes( workSurface );
/* get the index of the vertex that we're going to store at newVertIndex */ /* get the index of the vertex that we're going to store at newVertIndex */
vertDataIndex = PicoFindSurfaceVertexNum( workSurface, *xyz[i], *normals[i], numSTs, st[i], numColors, colors[i] ); vertDataIndex = PicoFindSurfaceVertexNum( workSurface, *xyz[i], *normals[i], numSTs, st[i], numColors, colors[i], smoothingGroup[i] );
/* the vertex wasn't found, so create a new vertex in the pool from the data we have */ /* the vertex wasn't found, so create a new vertex in the pool from the data we have */
if ( vertDataIndex == -1 ) { if ( vertDataIndex == -1 ) {
@ -2058,6 +2436,8 @@ void PicoAddTriangleToModel( picoModel_t *model, picoVec3_t** xyz, picoVec3_t**
{ {
PicoSetSurfaceST( workSurface, j, vertDataIndex, st[i][j] ); PicoSetSurfaceST( workSurface, j, vertDataIndex, st[i][j] );
} }
PicoSetSurfaceSmoothingGroup( workSurface, vertDataIndex, smoothingGroup[ i ] );
} }
/* add this vertex to the triangle */ /* add this vertex to the triangle */