brender-v1.3.2/tools/geoconv/geoconv.c
2022-05-03 13:10:51 -07:00

2667 lines
60 KiB
C

/*
* Copyright (c) 1993-1995 Argonaut Technologies Limited. All rights reserved.
*
* $Id: geoconv.c 1.4 1998/09/24 15:53:17 jon Exp $
* $Locker: $
*
* Command line interface for geometry file format convertion
*
* $BC<"make -f geoconv.mak %s.obj;">
*/
#define _NO_VECTOR_MACROS
#define BR_MPREP_ALL BR_MODU_ALL
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
#include <limits.h>
#include "brender.h"
#include "dosio.h"
#include "fmt.h"
#include "oldmodel.h"
#define VERBOSE 0
#define TYPE_GUESS -1
#define WRAP_U 0x40
#define WRAP_V 0x80
/*
* Known output file types
*/
void OutputModels(char *filename);
void OutputModelsTxt(char *filename);
void OutputModelsC(char *filename);
void OutputMaterials(char *filename);
void OutputMaterialsTxt(char *filename);
void OutputActors(char *filename);
void OutputActorsTxt(char *filename);
void OutputUnimplemented(char *filename);
void OutputASCFile(char *filename);
#if SAVE_3DS
void Output3DSModels(char *filename);
#endif
void OutputIRITModels(char *filename);
old_model * OldModel(br_model *model);
void MergeFromOldModel(old_model *model, br_model *output);
int FwAddEdge(br_uint_16 first, br_uint_16 last);
static struct pm_temp_edge {
struct pm_temp_edge *next; /* next in chain */
br_uint_16 first; /* First vertex */
br_uint_16 last; /* Last Vertex */
char other; /* Edge is used in other direction */
};
static struct pm_temp_edge *pm_edge_table;
static struct pm_temp_edge **pm_edge_hash;
static char *pm_edge_scratch;
static int num_edges = 0;
br_uint_32 BR_PUBLIC_ENTRY BrFmtIritModelSaveMany(char *name,
br_model **mtable, br_uint_16 nmodels);
struct {
char *name;
void (*function)(char *name);
} OutputFileTypes[] = {
/*
* Brender formats - file extentions
*/
{"dat", OutputModels},
{"brm", OutputModels},
{"brt", OutputMaterials},
{"bra", OutputActors},
{"brc", OutputUnimplemented},
{"brl", OutputUnimplemented},
{"brn", OutputUnimplemented},
/*
* Brender formats - verbose names
*/
{"models", OutputModels},
{"materials", OutputMaterials},
{"actors", OutputActors},
{"animation", OutputUnimplemented},
{"txt-models", OutputModelsTxt},
{"txt-materials", OutputMaterialsTxt},
{"txt-actors", OutputActorsTxt},
{"txt-animation", OutputUnimplemented},
{"c", OutputModelsC},
{"c-models", OutputModelsC},
{"c-materials", OutputUnimplemented},
{"c-actors", OutputUnimplemented},
/*
* 3DS formats
*/
{"asc", OutputASCFile},
/*
* Foreign formats
*/
#if SAVE_3DS
{"3ds", Output3DSModels},
{"3ds-materials",OutputUnimplemented},
#endif
{"irit", OutputIRITModels},
};
/*
* Use the model->user pointer to save store real flags
*/
#define MODEL_FLAGS(model) (*(br_uint_32 *)(&(model)->user))
/*
* Current output type
*/
int OutputType = TYPE_GUESS;
/*
* A hook function used to create unknown materials on demand
*/
br_material * BR_CALLBACK LoadMaterialFFHook(char *name);
/*
* Known input file types
*/
void InputBrender(char *filename);
void InputNFF(char *filename);
void InputASC(char *filename);
struct {
char *name;
void (*function)(char *name);
} InputFileTypes[] = {
{"txt", InputBrender},
{"dat", InputBrender},
{"nff", InputNFF},
{"asc", InputASC},
#if 0
{"3ds", Input3DS},
#endif
};
/*
* Current import type
*/
int InputType = TYPE_GUESS;
/*
* Sorting types
*/
enum output_sort_types {
SORT_OUT_NONE,
SORT_OUT_NAME,
};
int OutputSortType = SORT_OUT_NONE;
/*
* Structure used whilst merging near vertices
*/
struct merge_vertex {
float a[3];
int n;
};
struct apply_map {
int type;
int axes[2];
};
static int LookupAxis(char *s);
/*
* Various processing operations invoked from command line parameters
*/
br_uint_32 BR_CALLBACK ModelApplyMaterial(br_model *model, br_material *mat);
br_uint_32 BR_CALLBACK ModelApplyMap(br_model *model, struct apply_map *am);
br_uint_32 BR_CALLBACK ModelAxisRemap(br_model *model, int *axis_map);
br_uint_32 BR_CALLBACK ModelNormalise(br_model *model, void *dummy);
br_uint_32 BR_CALLBACK ModelCentre(br_model *model, void *dummy);
br_uint_32 BR_CALLBACK ModelFlipFaces(br_model *model, void *dummy);
br_uint_32 BR_CALLBACK ModelUnifyNormals(br_model *model, void *dummy);
br_uint_32 BR_CALLBACK ModelMergeVertices(br_model *model, float *tolerance);
br_uint_32 BR_CALLBACK ModelTranslate(br_model *model, float *translate);
br_uint_32 BR_CALLBACK ModelScale(br_model *model, float *scale);
br_uint_32 BR_CALLBACK ModelCleanup(br_model *model, void *dummy);
br_uint_32 BR_CALLBACK ModelSetFlags(br_model *model, int *flags);
br_uint_32 BR_CALLBACK ModelClearFlags(br_model *model, int *flags);
br_uint_32 BR_CALLBACK ModelSetSmoothing(br_model *model, void *dummy);
br_uint_32 BR_CALLBACK ModelRename(br_model *model, char *name);
br_uint_32 BR_CALLBACK MaterialRename(br_material *material, char *name);
br_uint_32 BR_CALLBACK ModelFixWrapping(br_model *model, int *mask);
br_uint_32 BR_CALLBACK ModelFixWrapping3DS(br_model *model, void *dummy);
struct model_list_totals {
int vertices;
int edges;
int faces;
};
br_uint_32 BR_CALLBACK ModelList(br_model *model, struct model_list_totals *totals);
br_model *ModelMerge(br_model *dest, br_model *src, br_matrix34 *xfm);
br_uint_32 BR_CALLBACK MaterialList(br_material *material, void *dummy);
/*
* Current pattern
*/
char *CurrentPattern = NULL;
void ViewModel(br_model * model) {
br_pixelmap * screen, *back;
br_pixelmap * depth;
br_actor *world, *object, *light, *camera;
br_material ** materials;
br_camera *camera_data;
br_scalar camera_dist;
int i;
world = BrActorAllocate(BR_ACTOR_NONE,NULL);
camera = BrActorAdd(world,BrActorAllocate(BR_ACTOR_CAMERA,NULL));
light = BrActorAdd(world,BrActorAllocate(BR_ACTOR_LIGHT,NULL));
BrLightEnable(light);
camera->t.type = BR_TRANSFORM_MATRIX34;
camera_data = (br_camera *)camera->type_data;
camera_dist = BR_DIV(model->radius, BR_SIN(camera_data->field_of_view/2));
BrMatrix34Translate(&camera->t.t.mat,BR_SCALAR(0.0),BR_SCALAR(0.0),camera_dist);
camera_data->hither_z = camera_dist - model->radius;
camera_data->yon_z = camera_dist + model->radius;
object = BrActorAdd(world,BrActorAllocate(BR_ACTOR_MODEL,NULL));
object->model = model;
object->material = NULL;
materials = BrMemAllocate((sizeof(*materials) * ((object->model)->nfaces)) , BR_MEMORY_MATERIAL_INDEX);
ModelSaveMaterials(object->model,materials);
ModelApplyMaterial(object->model,NULL);
object->t.type = BR_TRANSFORM_MATRIX34;
BrMatrix34RotateY(&object->t.t.mat,BR_ANGLE_DEG(30.0));
screen = DOSGfxBegin("MCGA,W:320,H:200,B:8");
DOSGfxPaletteSet(BrPixelmapLoad("std.pal"));
back = BrPixelmapMatch(screen, BR_PMMATCH_OFFSCREEN);
back->origin_x = back->width /2;
back->origin_y = back->height /2;
depth = BrPixelmapMatch(back, BR_PMMATCH_DEPTH_16);
BrZbBegin(screen->type, BR_PMT_DEPTH_16);
DOSKeyBegin();
while (!(DOSKeyTest(SC_SPACE,0,REPT_FIRST_DOWN))) {
BrPixelmapFill(back,0);
BrPixelmapFill(depth,0xFFFFFFFF);
BrMatrix34PreRotateX(&object->t.t.mat,BR_ANGLE_DEG(0.5));
BrMatrix34PreRotateY(&object->t.t.mat,BR_ANGLE_DEG(0.3));
BrMatrix34PreRotateZ(&object->t.t.mat,BR_ANGLE_DEG(0.1));
BrZbSceneRender(world,camera,back,depth);
BrPixelmapDoubleBuffer(screen,back);
}
DOSKeyEnd();
ModelRestoreMaterials(object->model,materials);
BrActorRemove(object);
BrActorRemove(camera);
BrLightDisable(light);
BrActorRemove(light);
BrActorFree(object);
BrActorFree(camera);
BrActorFree(light);
BrActorFree(world);
BrZbEnd();
DOSGfxEnd();
BrPixelmapFree(back);
BrPixelmapFree(depth);
}
int main(int argc, char **argv)
{
char *cp;
BR_BANNER("GEOCONV","1994-1995","$Revision: 1.4 $");
/*
* Initialise framework
*/
BrBegin();
/*
* Process argument list in order
*/
while (argv++, --argc) {
if (**argv == '-') {
/*
* Got one or more flags
*/
for (cp = *argv + 1; *cp; cp++) {
if (strchr("IoOastPrMNSFDLwRv", *cp)) {
argv++;
if(--argc == 0)
BR_ERROR1("The %c option requires an argument", *cp);
}
switch (*cp) {
default:
BR_ERROR1("unknown option '%c'\n", *cp);
break;
/*
* Usage
*/
case '?':
fprintf(stdout,
"Usage: geoconv {options}\n"
"Options are treated as commands executed in left-to-right order -\n"
"\n"
" <input-file> Load a file into current data\n"
" -I <input-type> Set input file type\n"
" -o <file> Generate output file from current data\n"
" -O <output-type> Set type of data to generate\n"
" -n Normalise models to radius of 1.0\n"
" -c Centre models on 0,0,0\n"
" -f Flip face normals\n"
" -F <flag> Set or clear model flag\n"
#if 0
" -u Unify face normals\n"
#endif
" -w <axes> Fix wrapped texture coordinates\n"
" -p Remove identical vertices\n"
" -P <float> Merge vertices within a given tolerance\n"
" -C Remove degenerate faces, unused vertices\n"
" and duplicate faces\n"
" -m Collapse current data to one actor and one model\n"
" -r <pattern> Restrict subsequent operations to things\n"
" matching <pattern>\n"
" -l List current data\n"
" -g Set each model to a different smoothing group\n"
" -S <sort-type> Set sorting on output\n"
" -N <material-name> Set all models faces to use named material\n"
" -N default Set all models faces to use default material\n"
" -M <map-type>,<axis>,<axis> Apply a default U,V mapping to models\n"
" -s <float> Uniform scale applied to models\n"
" -s <float>,<float>,<float> Non-uniform scale applied to models\n"
" -t <float>,<float>,<float> Translation applied to models\n"
" -a <axis>,<axis>,<axis> Remap axes\n"
" -D <name> Rename all models\n"
" -L <name> Rename materials\n"
" -R <old-name> <new-name> Rename a model matching <old-name> which may\n"
" include wildcards (* and ?), to <new-name>.\n"
" -v <name> View a model using the default material.\n"
);
fprintf(stdout,
"\n"
" <input-type> =\n"
" dat - P3D/Q3D model files\n"
" nff - Eric Haines' Neutral File Format\n"
" asc - 3D Studio .ASC file\n"
#if 0
" 3ds - 3D Studio file\n"
" geo - Videoscape-3D (and Argonaut variants)\n"
" obj - Wavefront object file\n"
" dxf - Autocad DXF Interchange files\n"
" vue - 3D Studio VUE animation file\n"
#endif
"\n"
" If a type is not specified, it will be guessed from the extension\n"
" of the filename.\n");
fprintf(stdout,
"\n"
" <output-type> =\n"
" models (.dat or .brm)\n"
" c-models Source code for model data structures\n"
" c-prep-models Source code for prepared models\n"
#if 0
" materials (.brt)\n"
" actors (.bra)\n"
" cameras (.brc)\n"
" lights (.brl)\n"
" animation (.brn)\n"
#endif
#if 0
" 3ds 3D Studio format\n"
" 3ds-materials\n"
#endif
);
fprintf(stdout,
"\n"
" <axis> =\n"
" x y z - positive input axes\n"
" +x +y +z - positive input axes\n"
" -x -y -z - negative input axes\n");
fprintf(stdout,
"\n"
" <map-axes> =\n"
" u \ U - fix wrapping along u axis\n"
" v \ V - fix wrapping along v axis\n"
" uv \ UV - fix wrapping along uv axes\n");
fprintf(stdout,
"\n"
" <map-type> =\n"
" none\n"
" disc\n"
" plane\n"
" cylinder\n"
" sphere\n");
fprintf(stdout,
"\n"
" <material-name> =\n"
" <string>\n"
" default\n");
fprintf(stdout,
"\n"
" <sort-type> =\n"
" none\n"
" name\n");
fprintf(stdout,
"\n"
" <flag> =\n"
" +d Set BR_MODF_DONT_WELD\n"
" -d Clear BR_MODF_DONT_WELD\n"
" +o Set BR_MODF_KEEP_ORIGNAL\n"
" -o Clear BR_MODF_KEEP_ORIGNAL\n"
" +t Set BR_MODF_GENERATE_TAGS\n"
" -t Clear BR_MODF_GENERATE_TAGS\n"
" +q Set BR_MODF_QUICK_UPDATE\n"
" -q Clear BR_MODF_QUICK_UPDATE\n");
break;
/*
* -r <pattern>
*/
case 'r':
if(!stricmp(*argv,"*"))
CurrentPattern = NULL;
else
CurrentPattern = *argv;
break;
/*
* -R <name> <new-name>
*/
case 'R': {
char name[30];
char newname[30];
br_model * tempmodel;
sscanf(*(argv++),"%s",name);
if (--argc <= 0)
BR_ERROR0("-R requires two arguments");
else {
sscanf(*argv,"%s",newname);
tempmodel = BrModelFind(name);
if (!tempmodel)
BR_ERROR1("No model matching '%s' to rename.",name);
else {
BrResFree(tempmodel->identifier);
tempmodel->identifier = BrMemStrDup(newname);
}
}
}
break;
/*
* -I <input-type>
*/
case 'I': {
int i;
for(i=0; i < BR_ASIZE(InputFileTypes) ; i++)
if(!stricmp(*argv,InputFileTypes[i].name))
break;
if(i >= BR_ASIZE(InputFileTypes))
BR_ERROR1("Unknown input type \"%s\"",*argv);
InputType = i;
break;
}
/*
* -O <output-type>
*/
case 'O': {
int i;
for(i=0; i < BR_ASIZE(OutputFileTypes) ; i++)
if(!stricmp(*argv,OutputFileTypes[i].name))
break;
if(i >= BR_ASIZE(OutputFileTypes))
BR_ERROR1("Unknown output type \"%s\"",*argv);
OutputType = i;
break;
}
/*
* -o <output_file>
*/
case 'o': {
int i,ot = OutputType;
char *cp;
if(ot == TYPE_GUESS && (cp = strrchr(*argv,'.'))) {
/*
* Guess import type based on extension
*/
for(i=0; i < BR_ASIZE(OutputFileTypes); i++)
if(!stricmp(OutputFileTypes[i].name,cp+1))
ot = i;
}
/*
* Complain if, by this point, we don't know how to process file
*/
if(ot == TYPE_GUESS)
BR_ERROR0("Unknown output file format");
OutputFileTypes[ot].function(*argv);
break;
}
/*
* -n
*/
case 'n':
BrModelEnum(CurrentPattern,ModelNormalise,NULL);
break;
/*
* -N <material-type>
*/
case 'N': {
br_material *mat;
br_material_find_cbfn *old_hook;
if(!stricmp(*argv,"default"))
mat = NULL;
else {
old_hook = BrMaterialFindHook(LoadMaterialFFHook);
mat = BrMaterialFind(*argv);
BrMaterialFindHook(old_hook);
}
BrModelEnum(CurrentPattern,ModelApplyMaterial,mat);
break;
}
/*
* -D <name>
*/
case 'D':
BrModelEnum(CurrentPattern,ModelRename,*argv);
break;
/*
* -L <name>
*/
case 'L':
BrMaterialEnum(CurrentPattern,MaterialRename,*argv);
break;
/*
* -c
*/
case 'c':
BrModelEnum(CurrentPattern,ModelCentre,NULL);
break;
/*
* -f
*/
case 'f':
BrModelEnum(CurrentPattern,ModelFlipFaces,NULL);
break;
/*
* -u
*/
case 'u':
BrModelEnum(CurrentPattern,ModelUnifyNormals,NULL);
break;
/*
* -g
*/
case 'g': {
int current = 0;
BrModelEnum(CurrentPattern,ModelSetSmoothing,&current);
}
break;
/*
* -p
*/
case 'p': {
float tolerance = 0.0;
BrModelEnum(CurrentPattern,ModelMergeVertices,&tolerance);
break;
}
/*
* -v
*/
case 'v' : {
char name[30];
br_model * model;
sscanf(*argv,"%s",name);
model = BrModelFind(name);
if (!model)
BR_ERROR1("No such model to view: '%s'",name);
ViewModel(model);
break;
}
/*
* -w
*/
case 'w': {
int mask = 0;
char *cp;
for(cp = *argv; *cp; cp++) {
if(*cp == 'u' || *cp == 'U')
mask |= WRAP_U;
if(*cp == 'v' || *cp == 'V')
mask |= WRAP_V;
if(*cp != 'v' && *cp !='V' && *cp != 'u' && *cp != 'U')
BR_ERROR1("Invalid map axis: '%c'",*cp);
}
BrModelEnum(CurrentPattern,ModelFixWrapping,&mask);
break;
}
/*
* -C
*/
case 'C':
BrModelEnum(CurrentPattern,ModelCleanup,NULL);
break;
/*
* -P
*/
case 'P': {
float tolerance = atof(*argv);
BrModelEnum(CurrentPattern,ModelMergeVertices,&tolerance);
break;
}
/*
* -m
*/
case 'm': {
br_model **mtable;
br_model *new;
int nmodels;
int i;
/*
* Build a table of all the current models
*/
nmodels = BrModelCount(CurrentPattern);
if(nmodels <= 0)
BR_ERROR0("No models loaded for merge");
mtable = BrMemCalloc(sizeof(*mtable),nmodels,BR_MEMORY_APPLICATION);
BrModelFindMany(CurrentPattern,mtable,nmodels);
/*
* Merge all remaining models into first on list
*/
for(i=1; i < nmodels; i++) {
BrModelRemove(mtable[i]);
ModelMerge(mtable[0],mtable[i],NULL);
BrModelFree(mtable[i]);
}
BrModelPrepare(mtable[0],BR_MPREP_ALL);
BrMemFree(mtable);
break;
}
/*
* -l
*/
case 'l': {
int n;
struct model_list_totals totals = { 0,0,0};
n = BrModelCount(CurrentPattern);
if(n > 0) {
printf("Models: %d\n",n);
printf(
" Verts Faces Edges DOTQ Radius Bounds\n");
BrModelEnum(CurrentPattern,ModelList,&totals);
if(n > 1)
printf(
"\n TOTAL %5d %5d %5d\n",
totals.vertices,
totals.faces,
totals.edges
);
}
n = BrMaterialCount(NULL);
if(n > 0) {
printf("Materials: %d\n",n);
BrMaterialEnum(CurrentPattern,MaterialList,NULL);
}
break;
}
/*
* -s <scale>
*/
case 's': {
int i;
float scale[3];
i = sscanf(*argv,"%g,%g,%g",
&scale[0],&scale[1],&scale[2]);
if(i == 1) {
scale[2] = scale[0];
scale[1] = scale[0];
} else if(i != 3)
BR_ERROR0("incorrect number of parameters for -s");
BrModelEnum(CurrentPattern,ModelScale,scale);
break;
}
/*
* -t <tx>,<ty>,<tz>
*/
case 't': {
int i;
float translate[3];
i = sscanf(*argv,"%g,%g,%g",
&translate[0],&translate[1],&translate[2]);
if(i != 3)
BR_ERROR0("incorrect number of parameters to -t");
BrModelEnum(CurrentPattern,ModelTranslate,translate);
break;
}
/*
* -a <axis>,<axis>,<axis>
*/
case 'a': {
char aname[3][3];
int axes[3];
int i;
i = sscanf(*argv,"%2[^,],%2[^,],%2[^,]",aname[0],aname[1],aname[2]);
if(i != 3)
BR_ERROR0("Incorrect number of axes");
for(i=0; i<3; i++)
axes[i] = LookupAxis(aname[i]);
BrModelEnum(CurrentPattern,ModelAxisRemap,axes);
break;
}
/*
* -M <map-type>,<axis>,<axis>
*/
case 'M': {
int i;
char map_name[10];
char aname[2][3];
struct apply_map am;
i = sscanf(*argv,"%9[^,],%2[^,],%2[^,]",map_name,aname[0],aname[1]);
if(i != 3)
BR_ERROR0("Incorrect number of arguments");
for(i=0; i<2; i++)
am.axes[i] = LookupAxis(aname[i]);
if(am.axes[0] == am.axes[1])
BR_ERROR0("Mapping axes must be different");
if(!stricmp("plane",map_name))
am.type = BR_APPLYMAP_PLANE;
else if(!stricmp("sphere",map_name))
am.type = BR_APPLYMAP_SPHERE;
else if(!stricmp("cylinder",map_name))
am.type = BR_APPLYMAP_CYLINDER;
else if(!stricmp("disc",map_name))
am.type = BR_APPLYMAP_DISC;
else if(!stricmp("none",map_name))
am.type = BR_APPLYMAP_NONE;
else
BR_ERROR1("Unknown map type '%s'",map_name);
BrModelEnum(CurrentPattern,ModelApplyMap,&am);
break;
}
/*
* -S <sort-type>
*/
case 'S':
if(!strcmp(*argv,"none"))
OutputSortType = SORT_OUT_NONE;
else if(!strcmp(*argv,"name"))
OutputSortType = SORT_OUT_NAME;
else
BR_ERROR0("unknown sort type");
break;
/*
* -F <flag>
*/
case 'F': {
int flag_mask = 0;
char *cp;
br_model_enum_cbfn *alter_flag;
/*
* Work out if setting or clearing
*/
switch((*argv)[0]) {
case '+':
alter_flag = ModelSetFlags;
break;
case '-':
alter_flag = ModelClearFlags;
break;
default:
BR_ERROR0("<flag> must start with + or -");
}
/*
* Work out flag bits
*/
if((*argv)[1] == '\0')
BR_ERROR0("No model flag type");
for(cp = (*argv)+1; *cp; cp++) {
switch(toupper(*cp)) {
case 'D':
flag_mask |= BR_MODF_DONT_WELD;
break;
case 'O':
flag_mask |= BR_MODF_KEEP_ORIGINAL;
break;
case 'T':
flag_mask |= BR_MODF_GENERATE_TAGS;
break;
case 'Q':
flag_mask |= BR_MODF_QUICK_UPDATE;
break;
default:
BR_ERROR1("Unknown model flag '%c'",*cp);
}
}
/*
* Go through models
*/
BrModelEnum(CurrentPattern,alter_flag,&flag_mask);
}
}
}
} else {
int i,it = InputType;
char *cp,*input_name = *argv;
/**
** process input file
**/
if(it == TYPE_GUESS && (cp = strrchr(input_name,'.'))) {
/*
* Guess import type based on extension
*/
for(i=0; i < BR_ASIZE(InputFileTypes); i++)
if(!stricmp(InputFileTypes[i].name,cp+1))
it = i;
}
/*
* Complain if, by this point, we don't know how to process file
*/
if(it == TYPE_GUESS)
BR_ERROR0("Unknown input file format");
InputFileTypes[it].function(input_name);
}
}
/*
* Close down and go home
*/
BrEnd();
return 0;
}
static int LookupAxis(char *s)
{
int i;
static struct {
char *name;
int a;
} axis_names[] = {
{"x",BR_FITMAP_PLUS_X},
{"y",BR_FITMAP_PLUS_Y},
{"z",BR_FITMAP_PLUS_Z},
{"+x",BR_FITMAP_PLUS_X},
{"+y",BR_FITMAP_PLUS_Y},
{"+z",BR_FITMAP_PLUS_Z},
{"-x",BR_FITMAP_MINUS_X},
{"-y",BR_FITMAP_MINUS_Y},
{"-z",BR_FITMAP_MINUS_Z},
};
for(i=0; i< BR_ASIZE(axis_names); i++)
if(!stricmp(axis_names[i].name,s))
return axis_names[i].a;
BR_ERROR1("Unknown axis: '%s'",s);
return 0;
}
void HideModelFlags(br_model *model)
{
MODEL_FLAGS(model) = model->flags;
model->flags = BR_MODF_KEEP_ORIGINAL;
}
void RestoreModelFlags(br_model *model)
{
model->flags = MODEL_FLAGS(model);
}
/*
* A finf failed hook for BrmaterialFind - creates a default
* material of the given name
*
* This means that material names will be preserved from input -> output
*/
br_material * BR_CALLBACK LoadMaterialFFHook(char *name)
{
br_material *mat;
mat = BrMaterialAllocate(name);
BrMaterialAdd(mat);
return mat;
}
void InputNFF(char *filename)
{
br_model *mp;
#if VERBOSE
printf("InputNFF(%s)\n",filename);
#endif
if((mp = BrFmtNFFLoad(filename)) == NULL)
BR_ERROR1("Could not load file \'%s\'",filename);
HideModelFlags(mp);
BrModelAdd(mp);
}
void InputASC(char *filename)
{
br_model *mtable[1024];
int nmodels,i;
br_material_find_cbfn *old_hook;
#if VERBOSE
printf("InputASC(%s)\n",filename);
#endif
/*
* Hook BrMaterialFind for the duration of the laod so that any
* unknnown materials have defaults created
*/
old_hook = BrMaterialFindHook(LoadMaterialFFHook);
nmodels = BrFmtASCLoad(filename,mtable,BR_ASIZE(mtable));
BrMaterialFindHook(old_hook);
if(nmodels == 0)
BR_ERROR1("Could not load any models from '%s'",filename);
for(i=0; i < nmodels; i++)
HideModelFlags(mtable[i]);
BrModelAddMany(mtable,nmodels);
}
void InputBrender(char *filename)
{
br_model *mtable[1024];
int nmodels,i;
br_material_find_cbfn *old_hook;
#if VERBOSE
printf("InputBrender(%s)\n",filename);
#endif
/*
* Hook BrMaterialFind for the duration of the load so that any
* unknnown materials have defaults created
*/
old_hook = BrMaterialFindHook(LoadMaterialFFHook);
nmodels = BrModelLoadMany(filename,mtable,BR_ASIZE(mtable));
BrMaterialFindHook(old_hook);
if(nmodels == 0)
BR_ERROR1("Could not load any models from '%s'",filename);
/*
* Hide the flags
*/
for(i=0; i < nmodels; i++)
HideModelFlags(mtable[i]);
BrModelAddMany(mtable,nmodels);
}
static int BR_CALLBACK CompareItemNames( const void * v1, const void * v2)
{
return strcmp( **(char ***)v1, **(char ***)v2);
}
void SortOutputPointers(void *table,int num)
{
switch(OutputSortType) {
case SORT_OUT_NONE:
/*
* Leave table as is
*/
break;
case SORT_OUT_NAME:
/*
* Sort by name
*/
BrQsort(table,num,sizeof(char **),CompareItemNames);
break;
}
}
void OutputModels(char *filename)
{
br_model **mtable;
int nmodels,n,i,j;
br_model * modelptr;
#if VERBOSE
printf("OutputModels(%s)\n",filename);
#endif
nmodels = BrModelCount(CurrentPattern);
mtable = BrMemCalloc(sizeof(*mtable),nmodels,BR_MEMORY_APPLICATION);
nmodels = BrModelFindMany(CurrentPattern,mtable,nmodels);
SortOutputPointers(mtable,nmodels);
for(i=0; i < nmodels; i++)
RestoreModelFlags(mtable[i]);
j = nmodels / 2;
// Reverse list of parts since BrModelFindMany returns reversed
// list as compared to input.
for(i=0; i < j; i++) {
modelptr = mtable[i];
mtable[i] = mtable[(nmodels-i)-1];
mtable[(nmodels-i)-1] = modelptr;
}
BrModelSaveMany(filename,mtable,nmodels);
for(i=0; i < nmodels; i++)
HideModelFlags(mtable[i]);
BrMemFree(mtable);
}
void OutputModelsTxt(char *filename)
{
br_model **mtable;
int nmodels,n,i;
int old_mode;
#if VERBOSE
printf("OutputModelsTxt(%s)\n",filename);
#endif
nmodels = BrModelCount(CurrentPattern);
mtable = BrMemCalloc(sizeof(*mtable),nmodels,BR_MEMORY_APPLICATION);
nmodels = BrModelFindMany(NULL,mtable,nmodels);
SortOutputPointers(mtable,nmodels);
for(i=0; i < nmodels; i++)
RestoreModelFlags(mtable[i]);
old_mode = BrWriteModeSet(BR_FS_MODE_TEXT);
BrModelSaveMany(filename,mtable,nmodels);
BrWriteModeSet(old_mode);
for(i=0; i < nmodels; i++)
HideModelFlags(mtable[i]);
BrMemFree(mtable);
}
void OutputMaterials(char *filename)
{
br_material **mtable;
int nmaterials,n,i,j;
br_material *matptr;
#if VERBOSE
printf("OutputMaterials(%s)\n",filename);
#endif
nmaterials = BrMaterialCount(NULL);
mtable = BrMemCalloc(sizeof(*mtable),nmaterials,BR_MEMORY_APPLICATION);
nmaterials = BrMaterialFindMany(NULL,mtable,nmaterials);
SortOutputPointers(mtable,nmaterials);
j = nmaterials /2;
// Reverse list of materials since BrMaterialFindMany returns
// reversed list as compared to input.
for(i=0; i < j; i++) {
matptr = mtable[i];
mtable[i] = mtable[(nmaterials-i)-1];
mtable[(nmaterials-i)-1] = matptr;
}
BrMaterialSaveMany(filename,mtable,nmaterials);
BrMemFree(mtable);
}
void OutputMaterialsTxt(char *filename)
{
br_material **mtable;
int nmaterials,n;
int old_mode;
#if VERBOSE
printf("OutputMaterials(%s)\n",filename);
#endif
nmaterials = BrMaterialCount(NULL);
mtable = BrMemCalloc(sizeof(*mtable),nmaterials,BR_MEMORY_APPLICATION);
nmaterials = BrMaterialFindMany(CurrentPattern,mtable,nmaterials);
SortOutputPointers(mtable,nmaterials);
old_mode = BrWriteModeSet(BR_FS_MODE_TEXT);
BrMaterialSaveMany(filename,mtable,nmaterials);
BrWriteModeSet(old_mode);
BrMemFree(mtable);
}
void OutputIRITModels(char *filename)
{
br_model **mtable;
int nmodels,n,i;
int old_mode;
#if VERBOSE
printf("OutputIRITModels(%s)\n",filename);
#endif
nmodels = BrModelCount(CurrentPattern);
mtable = BrMemCalloc(sizeof(*mtable),nmodels,BR_MEMORY_APPLICATION);
nmodels = BrModelFindMany(CurrentPattern,mtable,nmodels);
SortOutputPointers(mtable,nmodels);
for(i=0; i < nmodels; i++)
RestoreModelFlags(mtable[i]);
BrFmtIritModelSaveMany(filename,mtable,nmodels);
for(i=0; i < nmodels; i++)
HideModelFlags(mtable[i]);
BrMemFree(mtable);
}
void OutputActors(char *filename)
{
#if VERBOSE
printf("OutputActors(%s)\n",filename);
#endif
}
void OutputActorsTxt(char *filename)
{
#if VERBOSE
printf("OutputActorsTxt(%s)\n",filename);
#endif
}
void OutputUnimplemented(char *filename)
{
BR_ERROR0("Unimplemented Output type");
}
/*
* Produce a 3D-Stduio .ASC file
*/
void OutputASCFile(char *filename)
{
br_model **mtable;
int nmodels,i,j;
int a,b,c,ab,bc,ca,flag,smooth;
float vx,vy,vz,vu,vv;
FILE *fp;
fp=fopen(filename,"w");
nmodels = BrModelCount(CurrentPattern);
mtable = BrMemCalloc(sizeof(*mtable),nmodels,BR_MEMORY_APPLICATION);
nmodels = BrModelFindMany(CurrentPattern,mtable,nmodels);
for(j = 0 ; j < nmodels ; j ++){
fprintf(fp,"Named object: \"%s\"\n", mtable[j]->identifier);
fprintf(fp,"Tri-mesh, Vertices: %d Faces: %d\n" , mtable[j]->nvertices,mtable[j]->nfaces);
vu = mtable[j]->vertices[0].map.v[0];
vv = mtable[j]->vertices[0].map.v[1];
if(vu || vv) fprintf(fp,"Mapped\n");
fprintf(fp,"Vertex list:\n");
for(i=0 ; i < mtable[j]->nvertices ; i++){
vx = mtable[j]->vertices[i].p.v[0];
vy = mtable[j]->vertices[i].p.v[1];
vz = mtable[j]->vertices[i].p.v[2];
vu = mtable[j]->vertices[i].map.v[0];
vv = mtable[j]->vertices[i].map.v[1];
if(vu || vv){
fprintf(fp,"Vertex %d X: %f Y: %f Z: %f U: %f V: %f\n" , i , vx , vy , vz , vu , vv);
}
else{
fprintf(fp,"Vertex %d X: %f Y: %f Z: %f\n" , i , vx , vy , vz);
}
}
fprintf(fp,"Face list:\n");
for(i=0 ; i < mtable[j]->nfaces ; i++){
a = mtable[j]->faces[i].vertices[0];
b = mtable[j]->faces[i].vertices[1];
c = mtable[j]->faces[i].vertices[2];
flag = mtable[j]->faces[i].flags;
switch(flag){
case 0 : ab = 1; bc = 1; ca = 1;
break;
case 1 : ab = 0; bc = 1; ca = 1;
break;
case 2 : ab = 1; bc = 0; ca = 1;
break;
case 3 : ab = 0; bc = 0; ca = 1;
break;
case 4 : ab = 1; bc = 1; ca = 0;
break;
case 5 : ab = 0; bc = 1; ca = 0;
break;
case 6 : ab = 1; bc = 0; ca = 0;
break;
case 7 : ab = 0; bc = 0; ca = 0;
break;
}
fprintf(fp,"Face %d: A: %d B: %d C: %d AB: %d BC: %d CA: %d\n" , i , a , b , c , ab , bc , ca);
if(mtable[j]->faces[i].material)
if(mtable[j]->faces[i].material->identifier)
fprintf(fp,"material: \"%s\" \n" , mtable[j]->faces[i].material->identifier);
if(!(mtable[j]->faces[i].smoothing == (br_uint_16)~0))
fprintf(fp,"Smoothing: %d \n" , (int)(log10(mtable[j]->faces[i].smoothing)/log10(2)) +1);
}
}
fclose(fp);
BrMemFree(mtable);
}
#if 0
void PrintMatrix34(br_matrix34 *t)
{
int i,j;
for(i=0; i<4; i++) {
for(j=0; j<3; j++)
printf("%11.5f ",BrScalarToFloat(t->m[i][j]));
printf("\n");
}
}
#endif
br_uint_32 BR_CALLBACK ModelApplyMaterial(br_model *model, br_material *mat)
{
int i;
for(i=0; i< model->nfaces; i++)
model->faces[i].material = mat;
BrModelPrepare(model,BR_MPREP_ALL);
return 0;
}
void ModelSaveMaterials(br_model *model, br_material **mat)
{
int i;
for(i=0; i< model->nfaces; i++)
mat[i] = model->faces[i].material;
BrModelPrepare(model,BR_MPREP_ALL);
}
void ModelRestoreMaterials(br_model *model, br_material **mat)
{
int i;
for(i=0; i< model->nfaces; i++)
model->faces[i].material = mat[i];
BrModelPrepare(model,BR_MPREP_ALL);
}
br_uint_32 BR_CALLBACK ModelApplyMap(br_model *model, struct apply_map *am)
{
br_matrix34 map_xform;
/*
* Generate and apply the mapping transform
*/
BrModelFitMap(model,am->axes[0],am->axes[1],&map_xform);
BrModelApplyMap(model,am->type,&map_xform);
BrModelPrepare(model,BR_MPREP_ALL);
return 0;
}
br_uint_32 BR_CALLBACK ModelAxisRemap(br_model *model, int *axis_map)
{
int i,j;
br_vertex *vp;
br_vertex old_v;
#if VERBOSE
printf("ModelAxisRemap(%s,[%d,%d,%d])\n",model->identifier,
axis_map[0],axis_map[1],axis_map[2]);
#endif
for(i=0,vp=model->vertices; i<model->nvertices; i++,vp++) {
old_v = *vp;
for(j=0; j<3; j++)
if(axis_map[j] < 3)
vp->p.v[j] = old_v.p.v[axis_map[j]];
else
vp->p.v[j] = -old_v.p.v[axis_map[j]-3];
}
BrModelPrepare(model,BR_MPREP_ALL);
return 0;
}
br_uint_32 BR_CALLBACK ModelNormalise(br_model *model, void *dummy)
{
int v;
br_scalar max_r,r;
#if VERBOSE
printf("ModelNormalise(%s)\n",model->identifier);
#endif
max_r = BR_SCALAR(0.0);
for(v = 0; v < model->nvertices; v++) {
r = BrVector3Length(&model->vertices[v].p);
if(r> max_r)
max_r = r;
}
if(max_r == BR_SCALAR(0.0))
BR_ERROR0("Model has zero radius");
max_r = BR_DIV(BR_SCALAR(1.0),max_r);
for(v = 0; v < model->nvertices; v++) {
BrVector3Scale(&model->vertices[v].p,&model->vertices[v].p,max_r);
}
BrModelPrepare(model,BR_MPREP_ALL);
return 0;
}
#if 0
/*
* Use average of vertices
*/
br_uint_32 BR_CALLBACK ModelCentre(br_model *model, void *dummy)
{
int v;
br_scalar fx,fy,fz;
br_scalar vx,vy,vz;
#if VERBOSE
printf("ModelCentre(%s)\n",model->identifier);
#endif
/*
* Work out average of vertices, and translate object to there
*/
fx = fy = fz = 0.0;
for(v = 0; v < model->nvertices; v++) {
fx += BrScalarToFloat(model->vertices[v].p.v[0]);
fy += BrScalarToFloat(model->vertices[v].p.v[1]);
fz += BrScalarToFloat(model->vertices[v].p.v[2]);
}
vx = BrFloatToScalar(fx / model->nvertices);
vy = BrFloatToScalar(fy / model->nvertices);
vz = BrFloatToScalar(fz / model->nvertices);
for(v = 0; v < model->nvertices; v++) {
model->vertices[v].p.v[0] = BR_SUB(model->vertices[v].p.v[0],vx);
model->vertices[v].p.v[1] = BR_SUB(model->vertices[v].p.v[1],vy);
model->vertices[v].p.v[2] = BR_SUB(model->vertices[v].p.v[2],vz);
}
BrModelPrepare(model,BR_MPREP_ALL);
return 0;
}
#else
/*
* Use centre of bounds
*/
br_uint_32 BR_CALLBACK ModelCentre(br_model *model, void *dummy)
{
int v;
br_vector3 t;
#if VERBOSE
printf("ModelCentre(%s)\n",model->identifier);
#endif
BrVector3Add(&t,&model->bounds.max,&model->bounds.min);
BrVector3Scale(&t,&t,BR_SCALAR(-0.5));
for(v = 0; v < model->nvertices; v++) {
BrVector3Accumulate(&model->vertices[v].p,&t);
}
BrModelPrepare(model,BR_MPREP_ALL);
return 0;
}
#endif
br_uint_32 BR_CALLBACK ModelFlipFaces(br_model *model, void *dummy)
{
int f,t;
#if VERBOSE
printf("ModelFlipFaces(%s)\n",model->identifier);
#endif
/*
* Reverse order of vertices in each face
*/
for(f = 0; f < model->nfaces; f++) {
t = model->faces[f].vertices[0];
model->faces[f].vertices[0] = model->faces[f].vertices[2];
model->faces[f].vertices[2] = t;
t = model->faces[f].flags;
model->faces[f].flags &= BR_FACEF_COPLANAR_1;
if(t & BR_FACEF_COPLANAR_0)
model->faces[f].flags |= BR_FACEF_COPLANAR_2;
if(t & BR_FACEF_COPLANAR_2)
model->faces[f].flags |= BR_FACEF_COPLANAR_0;
}
BrModelPrepare(model,BR_MPREP_ALL);
return 0;
}
br_uint_32 BR_CALLBACK ModelUnifyNormals(br_model *model, void *dummy)
{
#if VERBOSE
printf("ModelUnifyNormals(%s)\n",model->identifier);
#endif
BrModelPrepare(model,BR_MPREP_ALL);
return 0;
}
/*
* Flags to ModelFixWrapping()
*/
#define WRAP_U 0x40
#define WRAP_V 0x80
/*
* Scratch structures used by ModelFixWrapping()
*/
struct wrap_face_info {
br_uint_8 edge_dir[3];
br_uint_8 vflags[3];
};
struct wrap_edge_info {
br_uint_16 old_vertices[2];
br_uint_16 vertices[2];
br_uint_8 count;
br_uint_8 flags;
br_uint_8 vflags[2];
};
/*
* Temp. workspace used to build new vertices
*/
static br_vertex *WrapNewVertices;
static int WrapTotalVertices;
static int WrapNextVertex;
/*
* Adds a new vertex to model that has had it's
* wrapping fixed
*/
static int WrapAddVertex(br_vertex *new_vp)
{
int i;
struct br_vertex *vp;
/*
* Try and find vertex in current set
*/
vp = WrapNewVertices;
for(i=0; i < WrapNextVertex; i++, vp++) {
if( new_vp->p.v[0] == vp->p.v[0] &&
new_vp->p.v[1] == vp->p.v[1] &&
new_vp->p.v[2] == vp->p.v[2] &&
new_vp->map.v[0] == vp->map.v[0] &&
new_vp->map.v[1] == vp->map.v[1])
return i;
}
/*
* Add to end of list
*/
if(WrapNextVertex >= WrapTotalVertices)
BR_ERROR0("WrapAddVertex: no space");
WrapNewVertices[WrapNextVertex] = *new_vp;
return WrapNextVertex++;
}
/*
* ModelFixWrapping()
*
* Processes a model and attempts to fix up seams where the texture
* coordinates implicitly wrap.
*
* Takes a pointer to an integer mask of [WRAP_U , WRAP_V]
* Each bit indicates that wrapping should be explored in that
* texture axis.
*/
br_uint_32 BR_CALLBACK ModelFixWrapping(br_model *input, int *mask)
{
old_model * model;
br_vertex *vp0,*vp1;
old_face *fp;
int f,e,e0,e1,v,vw,num_wraps,v0,v1;
struct wrap_edge_info *edge_info,*eip;
struct wrap_face_info *face_info,*fip;
br_vertex tempv;
model = OldModel(input);
edge_info = BrMemCalloc(model->nedges,sizeof(*edge_info),BR_MEMORY_APPLICATION);
face_info = BrMemCalloc(model->nfaces,sizeof(*face_info),BR_MEMORY_APPLICATION);
/*
* Go through faces to build edge table
*/
fp = model->faces;
fip = face_info;
for(f=0 ; f < model->nfaces; f++, fp++,fip++) {
for(e0 = 0; e0 < 3; e0++) {
e1 = (e0 == 2)?0:(e0 + 1);
eip = edge_info + fp->edges[e0];
eip->count++;
if(eip->count == 1) {
/*
* First time edge has been used
*/
eip->vertices[0] = fp->vertices[e0];
eip->vertices[1] = fp->vertices[e1];
eip->old_vertices[0] = eip->vertices[0];
eip->old_vertices[1] = eip->vertices[1];
}
/*
* See which direction edge is used
*/
if(eip->vertices[0] == fp->vertices[e0] &&
eip->vertices[1] == fp->vertices[e1]) {
fip->edge_dir[e0] = 0;
} else if(
eip->vertices[0] == fp->vertices[e1] &&
eip->vertices[1] == fp->vertices[e0]) {
fip->edge_dir[e0] = 1;
} else
BR_ERROR1("Face %d has a bad edge\n",f);
}
}
/*
* For each edge - find if U or V wraps
*/
num_wraps = 0;
eip = edge_info;
for(e=0; e< model->nedges; e++, eip++) {
vp0 = model->vertices + eip->vertices[0];
vp1 = model->vertices + eip->vertices[1];
if((*mask & WRAP_U) && (BR_ABS(vp0->map.v[0]-vp1->map.v[0]) > BR_SCALAR(0.5))) {
vw = (vp0->map.v[0] > vp1->map.v[0])?1:0;
/*
* Mark lower vertex as wrapping - keep a count
* new wraps
*/
if(!(eip->vflags[vw] & WRAP_U)) {
eip->vflags[vw] |= WRAP_U;
num_wraps++;
}
/*
* mark face
*/
eip->flags |= WRAP_U;
}
if((*mask & WRAP_V) && (BR_ABS(vp0->map.v[1]-vp1->map.v[1]) > BR_SCALAR(0.5))) {
vw = (vp0->map.v[1] > vp1->map.v[1])?1:0;
if(!(eip->vflags[vw] & WRAP_V)) {
eip->vflags[vw] |= WRAP_V;
num_wraps++;
}
/*
* mark face
*/
eip->flags |= WRAP_V;
}
}
/*
* Go through faces and accumulate per face-vertex flags
*/
fp = model->faces;
fip = face_info;
for(f=0; f< model->nfaces; f++, fp++, fip++) {
for(e0 = 0; e0 < 3; e0++) {
e1 = (e0 == 2)?0:(e0 + 1);
eip = edge_info + fp->edges[e0];
if(fip->edge_dir[e0])
v0 = 1, v1 = 0;
else
v0 = 0, v1 = 1;
fip->vflags[e0] |= eip->vflags[v0];
fip->vflags[e1] |= eip->vflags[v1];
}
}
#if 0
/*
* Dump faces, edges and vertices
*/
fp = model->faces;
fip = face_info;
printf("Before:\n");
for(f=0; f< model->nfaces; f++, fp++, fip++)
printf("Face %d V:[%d,%d,%d] E:[%d,%d,%d] DIRN:[%d,%d,%d] VF:[%02X,%02X,%02X]\n",
f,
fp->vertices[0],fp->vertices[1],fp->vertices[2],
fp->edges[0],fp->edges[1],fp->edges[2],
fip->edge_dir[0],fip->edge_dir[1],fip->edge_dir[2],
fip->vflags[0],fip->vflags[1],fip->vflags[2]);
printf("\n");
eip = edge_info;
for(e=0; e< model->nedges; e++, eip++)
printf("Edge: %d V:[%d,%d] C:%d F:%02x VF:[%02X,%02X] OV:[%d,%d]\n",
e,eip->vertices[0],eip->vertices[1],
eip->count,eip->flags,
eip->vflags[0],eip->vflags[1],
eip->old_vertices[0],eip->old_vertices[1]);
printf("\n");
vp0 = model->vertices;
for(v=0; v< model->nvertices; v++, vp0++)
printf("Vertex %d: P:(%g,%g,%g) M:(%g,%g)\n",
v,
BrScalarToFloat(vp0->p.v[0]),BrScalarToFloat(vp0->p.v[1]),BrScalarToFloat(vp0->p.v[2]),
BrScalarToFloat(vp0->map.v[0]),BrScalarToFloat(vp0->map.v[1]));
printf("\n");
#endif
/*
* Allocate a new vertex array (may be bigger than required)
*/
WrapTotalVertices = model->nvertices + num_wraps;
WrapNewVertices = BrResAllocate(model,WrapTotalVertices * sizeof(*WrapNewVertices),BR_MEMORY_VERTICES);
memcpy(WrapNewVertices,model->vertices,sizeof(*WrapNewVertices) * model->nvertices);
WrapNextVertex = model->nvertices;
/*
* Go through faecs and generate new vertices
*/
fp = model->faces;
fip = face_info;
for(f=0; f< model->nfaces; f++, fp++, fip++) {
for(v=0; v < 3; v++) {
if(!(fip->vflags))
continue;
/*
* Make a copy of the vertex, and wrap
* mapping coordinates
*/
tempv = model->vertices[fp->vertices[v]];
if(fip->vflags[v] & WRAP_U)
tempv.map.v[0] = tempv.map.v[0] + BR_SCALAR(1.0);
if(fip->vflags[v] & WRAP_V)
tempv.map.v[1] = tempv.map.v[1] + BR_SCALAR(1.0);
fp->vertices[v] = WrapAddVertex(&tempv);
}
}
#if 0
/*
* Dump faces and edges
*/
fp = model->faces;
fip = face_info;
printf("After:\n");
for(f=0; f< model->nfaces; f++, fp++, fip++)
printf("Face %d V:[%d,%d,%d] E:[%d,%d,%d] DIRN:[%d,%d,%d]\n",
f,
fp->vertices[0],fp->vertices[1],fp->vertices[2],
fp->edges[0],fp->edges[1],fp->edges[2],
fip->edge_dir[0],fip->edge_dir[1],fip->edge_dir[2]);
printf("\n");
eip = edge_info;
for(e=0; e< model->nedges; e++, eip++)
printf("Edge: %d V:[%d,%d] C:%d F:%02x VF:[%02X,%02X] OV:[%d,%d]\n",
e,eip->vertices[0],eip->vertices[1],
eip->count,eip->flags,
eip->vflags[0],eip->vflags[1],
eip->old_vertices[0],eip->old_vertices[1]);
printf("\n");
vp0 = WrapNewVertices;
for(v=0; v< WrapNextVertex; v++, vp0++)
printf("Vertex %d: P:(%g,%g,%g) M:(%g,%g)\n",
v,
BrScalarToFloat(vp0->p.v[0]),BrScalarToFloat(vp0->p.v[1]),BrScalarToFloat(vp0->p.v[2]),
BrScalarToFloat(vp0->map.v[0]),BrScalarToFloat(vp0->map.v[1]));
printf("\n");
printf("New Vertices = %d (out of %d) Old = %d\n",WrapNextVertex,WrapTotalVertices,model->nvertices);
#endif
BrMemFree(face_info);
BrMemFree(edge_info);
/*
* Connect new vertex array to faces
*/
BrResFree(model->vertices);
model->vertices = WrapNewVertices;
model->nvertices = WrapNextVertex;
WrapNewVertices = NULL;
MergeFromOldModel(model,input);
BrModelPrepare(input,BR_MPREP_ALL);
BrResFree(model);
return 0;
}
/*
* Take face wrap flags from 3DS info and fix up wrapping
*/
br_uint_32 BR_CALLBACK ModelFixWrapping3DS(br_model *model, void *dummy)
{
br_face *fp;
br_vertex tempv;
int f,v;
/*
* Allocate a new vertex array (bigger than required)
*/
WrapTotalVertices = model->nfaces * 3;
WrapNewVertices = BrResAllocate(model,WrapTotalVertices * sizeof(*WrapNewVertices),BR_MEMORY_VERTICES);
memcpy(WrapNewVertices,model->vertices,sizeof(*WrapNewVertices) * model->nvertices);
WrapNextVertex = model->nvertices;
/*
* Go through faces and generate new vertices
*/
fp = model->faces;
for(f=0; f< model->nfaces; f++, fp++) {
if(!(fp->flags & (WRAP_U | WRAP_V)))
continue;
for(v=0; v < 3; v++) {
/*
* Make a copy of the vertex, and wrap
* mapping coordinates
*/
tempv = model->vertices[fp->vertices[v]];
if((fp->flags & WRAP_U) &&
(tempv.map.v[0] < BR_SCALAR(0.5)))
tempv.map.v[0] = tempv.map.v[0] + BR_SCALAR(1.0);
#if 0
if((fp->flags & WRAP_V) &&
(tempv.map.v[1] < BR_SCALAR(0.5)))
tempv.map.v[1] = tempv.map.v[1] + BR_SCALAR(1.0);
#endif
fp->vertices[v] = WrapAddVertex(&tempv);
}
}
/*
* Connect new vertex array to faces
*/
BrResFree(model->vertices);
model->vertices = WrapNewVertices;
model->nvertices = WrapNextVertex;
WrapNewVertices = NULL;
BrModelPrepare(model,BR_MPREP_ALL);
return 0;
}
/*
* qsort function to compare faces
*/
static int BR_CALLBACK CompareFacePtrs( const void * v1, const void * v2)
{
const br_face **p1 = (const br_face **)v1,**p2 = (const br_face **)v2;
if((*p1)->vertices[0] < (*p2)->vertices[0])
return -1;
else if((*p1)->vertices[0] > (*p2)->vertices[0])
return 1;
else if((*p1)->vertices[1] < (*p2)->vertices[1])
return -1;
else if((*p1)->vertices[1] > (*p2)->vertices[1])
return 1;
else if((*p1)->vertices[2] < (*p2)->vertices[2])
return -1;
else if((*p1)->vertices[2] > (*p2)->vertices[2])
return 1;
else if((*p1)->material < (*p2)->material)
return -1;
else if((*p1)->material > (*p2)->material)
return 1;
else
return 0;
}
br_uint_32 BR_CALLBACK ModelCleanup(br_model *model, void *dummy)
{
int i,j,n;
br_face *fp,*fp_last,**sorted_faces;
br_vertex *v0,*v1,*v2;
int *vertex_counts;
int *vertex_mapping;
char *face_flags;
#if VERBOSE
printf("ModelCleanup(%s)\n",model->identifier);
#endif
/**
** Remove degenerate faces
**/
n = model->nfaces;
for(i=0, fp=model->faces; i< model->nfaces; ) {
/*
* If any two vertices on the face are the same, remove it
*
* Compare actual x,y,z values in case of duplicate vertices
*/
v0 = model->vertices + fp->vertices[0];
v1 = model->vertices + fp->vertices[1];
v2 = model->vertices + fp->vertices[2];
if((v0->p.v[0] == v1->p.v[0] &&
v0->p.v[1] == v1->p.v[1] &&
v0->p.v[2] == v1->p.v[2]) ||
(v0->p.v[0] == v2->p.v[0] &&
v0->p.v[1] == v2->p.v[1] &&
v0->p.v[2] == v2->p.v[2]) ||
(v2->p.v[0] == v1->p.v[0] &&
v2->p.v[1] == v1->p.v[1] &&
v2->p.v[2] == v1->p.v[2])) {
/*
* Move face at and of array to this slot (and don't advance
* to next face)
*/
model->nfaces--;
*fp = model->faces[model->nfaces];
} else {
/*
* Advance to next face
*/
i++;
fp++;
}
}
if(n != model->nfaces)
printf("Remove %d degenerate faces\n",n-model->nfaces);
/**
** Remove identical faces
**/
sorted_faces = BrMemCalloc(sizeof(*sorted_faces),model->nfaces,BR_MEMORY_APPLICATION);
face_flags = BrMemCalloc(sizeof(*face_flags),model->nfaces,BR_MEMORY_APPLICATION);
/*
* Sort model's faces by vertex numbers and material pointer
*/
for(i=0; i< model->nfaces; i++)
sorted_faces[i] = model->faces+i;
BrQsort(sorted_faces, model->nfaces, sizeof(*sorted_faces), CompareFacePtrs);
/*
* Go through sorted faces marking those that should be removed
*/
fp_last = sorted_faces[0];
face_flags[0] = 0;
for(i=1; i< model->nfaces; i++) {
fp = sorted_faces[i];
face_flags[fp-model->faces] =
fp->vertices[0] == fp_last->vertices[0] &&
fp->vertices[1] == fp_last->vertices[1] &&
fp->vertices[2] == fp_last->vertices[2] &&
fp->material == fp_last->material;
fp_last = fp;
}
/*
* Strip out identical faces
*/
for(i=0,j=0; i< model->nfaces; i++) {
if(face_flags[i] != 0)
continue;
model->faces[j] = model->faces[i];
j++;
}
if(model->nfaces > j)
printf("Remove %d identical faces\n",model->nfaces-j);
model->nfaces = j;
BrMemFree(face_flags);
BrMemFree(sorted_faces);
/**
** Remove unused vertices
**/
/*
* Make a count of vertex references
*/
vertex_counts = BrMemCalloc(sizeof(*vertex_counts),model->nvertices,BR_MEMORY_APPLICATION);
vertex_mapping = BrMemCalloc(sizeof(*vertex_mapping),model->nvertices,BR_MEMORY_APPLICATION);
for(i=0, fp=model->faces; i< model->nfaces; i++, fp++) {
vertex_counts[fp->vertices[0]]++;
vertex_counts[fp->vertices[1]]++;
vertex_counts[fp->vertices[2]]++;
}
/*
* Remove vertices with a count of 0, and make a mapping table
* of old->new
*/
j = 0;
for(i=0; i< model->nvertices; i++) {
if(vertex_counts[i] == 0)
continue;
/*
* This vertex is used - move it to new position
* and record mapping
*/
vertex_mapping[i] = j;
model->vertices[j] = model->vertices[i];
j++;
}
if(model->nvertices > j)
printf("Remove %d unreferenced vertices\n",model->nvertices-j);
model->nvertices = j;
/*
* Update faces to use new vertex numbers
*/
for(i=0, fp=model->faces; i< model->nfaces; i++, fp++) {
fp->vertices[0] = vertex_mapping[fp->vertices[0]];
fp->vertices[1] = vertex_mapping[fp->vertices[1]];
fp->vertices[2] = vertex_mapping[fp->vertices[2]];
}
BrMemFree(vertex_mapping);
BrMemFree(vertex_counts);
BrModelPrepare(model,BR_MPREP_ALL);
return 0;
}
/*
* qsort function to compare merge vertices
*/
static int BR_CALLBACK CompareMergeVertex( const void * v1, const void * v2)
{
const struct merge_vertex *p1 = v1,*p2 = v2;
if(p1->a[0] < p2->a[0])
return -1;
else if(p1->a[0] > p2->a[0])
return 1;
else
return 0;
}
br_uint_32 BR_CALLBACK ModelMergeVertices(br_model *model, float *pt)
{
struct br_vertex *vp;
struct br_face *fp;
struct merge_vertex *sorted;
struct merge_vertex *ip,*op;
int *output;
int *mapping;
float t = *pt,mt = -t, sqt = t*t;
int axis_0,axis_1,axis_2;
float d0,d1,d2;
int i,j,otail = 0,ohead = 0;
#if VERBOSE
printf("ModelMergeVertices(%f)\n",t);
#endif
sorted = BrMemCalloc(sizeof(*sorted),model->nvertices,BR_MEMORY_APPLICATION);
output = BrMemCalloc(sizeof(*output),model->nvertices,BR_MEMORY_APPLICATION);
mapping = BrMemCalloc(sizeof(*mapping),model->nvertices,BR_MEMORY_APPLICATION);
/*
* Fix order of axes for the moment - XXX should pick
*/
axis_0 = 0;
axis_1 = 1;
axis_2 = 2;
/*
* Build a sorted table of the vertices
*/
for(i=0, vp = model->vertices, ip =sorted;
i < model->nvertices;
i++, ip++, vp++) {
ip->a[0] = BrScalarToFloat(vp->p.v[axis_0]);
ip->a[1] = BrScalarToFloat(vp->p.v[axis_1]);
ip->a[2] = BrScalarToFloat(vp->p.v[axis_2]);
ip->n = i;
}
BrQsort(sorted, model->nvertices, sizeof(*sorted), CompareMergeVertex);
/*
* Do a plane sweep along chosen axis
*/
/*
* For each input vertex ...
*/
for(i = 0, ip = sorted; i< model->nvertices; i++, ip++) {
/*
* For each vertex in current window ...
*/
for(j = ohead-1; j >= otail; j--) {
op = sorted+output[j];
/*
* If out of tolerance on sweep axis, then no further
* vertices in window will be within tolerance
*/
d0 = ip->a[0] - op->a[0];
if(d0 > t || d0 < mt) {
otail = j+1;
break;
}
/*
* Do remaining axes - and check manhattan distance
*/
d1 = ip->a[1] - op->a[1];
if(d1 > t || d1 < mt)
continue;
d2 = ip->a[2] - op->a[2];
if(d2 > t || d2 < mt)
continue;
/*
* Check euclidian distance
*/
if(d0*d0 + d1*d1 + d2*d2 > sqt)
continue;
/*
* Merge input with current output
*/
mapping[ip->n] = j;
goto next_input;
}
/*
* Add vertex to output set
*/
mapping[ip->n] = ohead;
output[ohead++] = i;
next_input: ;
}
if(ohead < model->nvertices) {
printf("Removed %d vertices\n",model->nvertices-ohead);
/*
* Go through faces and remap vertex numbers
*/
for(i=0, fp=model->faces ; i< model->nfaces; i++, fp++) {
fp->vertices[0] = mapping[fp->vertices[0]];
fp->vertices[1] = mapping[fp->vertices[1]];
fp->vertices[2] = mapping[fp->vertices[2]];
}
/*
* Build new vertex table
*/
vp = BrResAllocate(model, sizeof(*vp) * ohead,BR_MEMORY_APPLICATION);
for(i = 0; i< ohead; i++)
vp[i] = model->vertices[sorted[output[i]].n];
BrResFree(model->vertices);
model->vertices = vp;
model->nvertices = ohead;
}
/*
* Release workspace
*/
BrMemFree(sorted);
BrMemFree(output);
BrMemFree(mapping);
BrModelPrepare(model,BR_MPREP_ALL);
return 0;
}
br_uint_32 BR_CALLBACK ModelTranslate(br_model *model, float *translate)
{
int v;
#if VERBOSE
printf("ModelTranslate(%s) tx=%f ty=%f tz=%f\n",
model->identifier,
translate[0],translate[1],translate[2]);
#endif
for(v = 0; v < model->nvertices; v++) {
model->vertices[v].p.v[0] = BR_ADD(model->vertices[v].p.v[0],BrFloatToScalar(translate[0]));
model->vertices[v].p.v[1] = BR_ADD(model->vertices[v].p.v[1],BrFloatToScalar(translate[1]));
model->vertices[v].p.v[2] = BR_ADD(model->vertices[v].p.v[2],BrFloatToScalar(translate[2]));
}
BrModelPrepare(model,BR_MPREP_ALL);
return 0;
}
br_uint_32 BR_CALLBACK ModelScale(br_model *model, float *scale)
{
int v;
#if VERBOSE
printf("ModelScale(%s) sx=%f sy=%f sz=%f\n",
model->identifier,
scale[0],scale[1],scale[2]);
#endif
for(v = 0; v < model->nvertices; v++) {
model->vertices[v].p.v[0] = BR_MUL(model->vertices[v].p.v[0],BrFloatToScalar(scale[0]));
model->vertices[v].p.v[1] = BR_MUL(model->vertices[v].p.v[1],BrFloatToScalar(scale[1]));
model->vertices[v].p.v[2] = BR_MUL(model->vertices[v].p.v[2],BrFloatToScalar(scale[2]));
}
BrModelPrepare(model,BR_MPREP_ALL);
return 0;
}
br_uint_32 BR_CALLBACK ModelList(br_model *input, struct model_list_totals *totals)
{
old_model * model = OldModel(input);
printf(" %-16s %5d %5d %5d %c%c%c%c %6.5g (%4.3g,%4.3g,%4.3g) to (%4.3g,%4.3g,%4.3g)\n",
model->identifier?model->identifier:"<NULL>",
model->nvertices,
model->nfaces,
model->nedges,
(MODEL_FLAGS(model) & BR_MODF_DONT_WELD)?'D':'-',
(MODEL_FLAGS(model) & BR_MODF_KEEP_ORIGINAL)?'O':'-',
(MODEL_FLAGS(model) & BR_MODF_GENERATE_TAGS)?'T':'-',
(MODEL_FLAGS(model) & BR_MODF_QUICK_UPDATE)?'Q':'-',
BrScalarToFloat(model->radius),
BrScalarToFloat(model->bounds.min.v[0]),
BrScalarToFloat(model->bounds.min.v[1]),
BrScalarToFloat(model->bounds.min.v[2]),
BrScalarToFloat(model->bounds.max.v[0]),
BrScalarToFloat(model->bounds.max.v[1]),
BrScalarToFloat(model->bounds.max.v[2])
);
/*
* If required, add counts to running total
*/
if(totals) {
totals->vertices += model->nvertices;
totals->edges += model->nedges;
totals->faces += model->nfaces;
}
BrResFree(model);
return 0;
}
/*
* Add source model's vertices and faces to destination
* model - optionally push each of the source models vertices
* though a supplied transform (if not NULL)
*/
br_model *ModelMerge(br_model *dest, br_model *src, br_matrix34 *xfm)
{
br_face *new_faces,*dest_fp;
br_vertex *new_vertices,*dest_vp;
int i;
#if VERBOSE
printf("ModelMerge(\"%s\",\"%s\")\n",dest->identifier,src->identifier);
#endif
/*
* make new arrays to hold both model's data
*/
new_vertices = BrResAllocate(dest,2 * sizeof(*new_vertices) * (dest->nvertices+src->nvertices), BR_MEMORY_APPLICATION);
new_faces = BrResAllocate(dest,2* sizeof(*new_faces) * (dest->nfaces+src->nfaces), BR_MEMORY_APPLICATION);
dest_fp = new_faces;
dest_vp = new_vertices;
/*
* Add first model
*/
for(i=0; i < dest->nvertices; i++,dest_vp++)
*dest_vp = dest->vertices[i];
for(i=0; i < dest->nfaces; i++,dest_fp++)
*dest_fp = dest->faces[i];
/*
* Add second model
*/
for(i=0; i < src->nvertices; i++,dest_vp++)
*dest_vp = src->vertices[i];
for(i=0; i < src->nfaces; i++,dest_fp++) {
*dest_fp = src->faces[i];
dest_fp->vertices[0] += dest->nvertices;
dest_fp->vertices[1] += dest->nvertices;
dest_fp->vertices[2] += dest->nvertices;
}
/*
* Release old tables
*/
if(dest->vertices)
BrResFree(dest->vertices);
if(dest->faces)
BrResFree(dest->faces);
/*
* Change dest model to point at the new merged data
*/
dest->vertices = new_vertices;
dest->nvertices += src->nvertices;
dest->faces = new_faces;
dest->nfaces += src->nfaces;
return dest;
}
br_uint_32 BR_CALLBACK MaterialList(br_material *material, void *dummy)
{
printf(" %s\n",material->identifier?material->identifier:"<NULL>");
return 0;
}
br_uint_32 BR_CALLBACK ModelSetFlags(br_model *model, int *flags)
{
MODEL_FLAGS(model) |= *flags;
return 0;
}
br_uint_32 BR_CALLBACK ModelClearFlags(br_model *model, int *flags)
{
MODEL_FLAGS(model) &= ~*flags;
BrModelPrepare(model,BR_MPREP_ALL);
return 0;
}
br_uint_32 BR_CALLBACK ModelSetSmoothing(br_model *model, int *current_group)
{
int f,s;
s = 1 << (*current_group % 16);
for(f=0; f< model->nfaces; f++)
model->faces[f].smoothing = s;
(*current_group)++;
BrModelPrepare(model,BR_MPREP_ALL);
return 0;
}
br_uint_32 BR_CALLBACK ModelRename(br_model *model, char *name)
{
if(model->identifier)
BrResFree(model->identifier);
if(name)
model->identifier = BrResStrDup(model,name);
else
model->identifier = NULL;
return 0;
}
br_uint_32 BR_CALLBACK MaterialRename(br_material *material, char *name)
{
if(material->identifier)
BrResFree(material->identifier);
if(name)
material->identifier = BrResStrDup(material,name);
else
material->identifier = NULL;
return 0;
}
/*
* Build edge references for each face
*/
old_model * OldModel(br_model *model)
{
br_size_t scratch_size;
old_face *fp;
int f,g;
old_model * output;
output = BrResAllocate(NULL,sizeof(old_model),BR_MEMORY_MODEL);
output->identifier = BrMemStrDup(model->identifier);
output->nvertices = model->nvertices;
output->vertices = BrResAllocate(output,output->nvertices *
sizeof(br_vertex),BR_MEMORY_VERTICES);
for (f=0; f < model->nvertices; f++) {
output->vertices[f] = model->vertices[f];
}
output->nfaces = model->nfaces;
output->faces = BrResAllocate(output,output->nfaces *
sizeof(old_face),BR_MEMORY_FACES);
for (f=0; f < model->nfaces; f++) {
output->faces[f].material = model->faces[f].material;
output->faces[f].smoothing = model->faces[f].smoothing;
output->faces[f].flags = model->faces[f].flags;
output->faces[f].n = model->faces[f].n;
output->faces[f].d = model->faces[f].d;
for (g=0; g < 3; g++) {
output->faces[f].vertices[g] = model->faces[f].vertices[g];
}
}
output->pivot = model->pivot;
output->flags = model->flags;
output->custom = model->custom;
output->user = model->user;
output->radius = model->radius;
output->bounds = model->bounds;
scratch_size =
output->nfaces * 3 * sizeof(*pm_edge_table) +
output->nvertices * sizeof(*pm_edge_hash);
pm_edge_scratch = BrScratchAllocate(scratch_size);
/*
* Divvy up scratch area and clear hash pointers
*/
pm_edge_hash = (struct pm_temp_edge **)pm_edge_scratch;
pm_edge_table = (struct pm_temp_edge *)
(pm_edge_scratch+output->nvertices * sizeof(*pm_edge_hash));
memset(pm_edge_hash,0,output->nvertices * sizeof(*pm_edge_hash));
/*
* Accumulate shared edges from each face
*/
num_edges = 0;
fp = output->faces;
for(f = 0; f < output->nfaces; f++, fp++) {
fp->edges[0] = FwAddEdge(fp->vertices[0],fp->vertices[1]);
fp->edges[1] = FwAddEdge(fp->vertices[1],fp->vertices[2]);
fp->edges[2] = FwAddEdge(fp->vertices[2],fp->vertices[0]);
}
output->nedges = num_edges;
BrScratchFree(pm_edge_scratch);
return output;
}
void MergeFromOldModel(old_model * model, br_model * output)
{
int f,g;
output->identifier = BrMemStrDup(model->identifier);
if (output->nvertices != model->nvertices) {
BrResFree(output->vertices);
output->nvertices = model->nvertices;
output->vertices = BrResAllocate(output,sizeof(br_vertex)*
output->nvertices,BR_MEMORY_VERTICES);
}
for (f=0; f < model->nvertices; f++) {
output->vertices[f] = model->vertices[f];
}
if (output->nfaces != model->nfaces) {
BrResFree(output->faces);
output->nfaces = model->nfaces;
output->faces = BrResAllocate(output,sizeof(br_face)*
output->nfaces,BR_MEMORY_FACES);
}
for (f=0; f < model->nfaces; f++) {
output->faces[f].material = model->faces[f].material;
output->faces[f].smoothing = model->faces[f].smoothing;
output->faces[f].flags = model->faces[f].flags;
output->faces[f].n = model->faces[f].n;
output->faces[f].d = model->faces[f].d;
for (g=0; g < 3; g++) {
output->faces[f].vertices[g] = model->faces[f].vertices[g];
}
}
output->pivot = model->pivot;
output->flags = model->flags;
output->custom = model->custom;
output->user = model->user;
output->radius = model->radius;
output->bounds = model->bounds;
}
static int FwAddEdge(br_uint_16 first, br_uint_16 last)
{
struct pm_temp_edge *tep;
/*
* See if edge exists and can be used in other direction
*/
for(tep = pm_edge_hash[last]; tep; tep = tep->next) {
if(tep->last == first && tep->other == 0) {
/*
* Yup, flag as used and return index
*/
tep->other = 1;
return tep - pm_edge_table;
}
}
/*
* Create new edge
*/
tep = pm_edge_table + num_edges;
tep->first = first;
tep->last = last;
tep->other = 0;
tep->next = pm_edge_hash[first];
pm_edge_hash[first] = tep;
return num_edges++;
}