From 3645f3f061378431b1502944fd347dc0b41af2e1 Mon Sep 17 00:00:00 2001 From: jmbaud Date: Sun, 18 Aug 2024 17:31:35 +0200 Subject: [PATCH] Add a -do-not-transform option --- tools/v3dconverter/readme.txt | 15 +- tools/v3dconverter/src/objtohf.c | 196 ++++++++++---------- tools/v3dconverter/src/objtov3d.c | 229 ++++++++++++------------ tools/v3dconverter/src/utils.c | 68 +++---- tools/v3dconverter/src/v3dconverter.c | 28 ++- tools/v3dconverter/src/v3dconverter.h | 12 +- tools/v3dconverter/src/v3dhftoobj.c | 248 ++++++++++++++------------ tools/v3dconverter/src/v3dtoobj.c | 103 ++++++----- 8 files changed, 476 insertions(+), 423 deletions(-) diff --git a/tools/v3dconverter/readme.txt b/tools/v3dconverter/readme.txt index 25de064..fde6585 100644 --- a/tools/v3dconverter/readme.txt +++ b/tools/v3dconverter/readme.txt @@ -1,24 +1,24 @@ - + +--------------------------+ | V3dconverter quick start | +--------------------------+ * FOREWORD: This tools is under development. Only *.obj to *.3d conversion is fully operational at now. Convert *.3d terrain to *.obj in order to edit an height field seems to gives good results too. Convert other *.3d objects to *.obj or convert *.ac to *.3d will only work if you're lucky, therefore for now please report bugs only for *.obj to *.3d conversion. I do 99% of my testing by exporting files from Blender in *.obj format, then converting them to *.3d format with v3dconverter. - + * DESCRIPTION: - V3dconverter is a file converter for Sar2 developers. Its purpose is to convert *.3d (Vertex 3d) model files from/to *.obj (WaveFront) or *.ac (AC3D) files. + V3dconverter is a file converter for Sar2 developers. Its purpose is to convert *.3d (Vertex 3d) model or *.hf (terrain) files from/to *.obj (WaveFront) or *.ac (AC3D) files. * HOW TO COMPILE: cd /path/to/v3dconverter - gcc -lm -Wall -o v3dconverter v3dconverter.c + gcc -lm -Wall -o v3dconverter ./src/v3dconverter.c * HOW TO USE: ./v3dconverter -h * IMPORTANT NOTES: - Don't stole 3D models. Create a model can take a lot of hours/days/months of hard work. Ensure that original model is free before add it in Sar2. If you're not sure of that, please try to contact model author and kindly ask him for permission to reuse it in Sar2. - - Even if v3dconverter can convert *.ac to *.3d and even if FlightGear models are generally free, it is not a very good idea to become a "FlightGear models serial converter". FlightGear models -especially flying ones- can be very detailed, generally too much detailed for Sar2. A better way is to open and simplificate them as much as possible (I use mainly Blender and / or sometimes Meshlab to do that) BEFORE convert them to *.3d format. + - Even if v3dconverter can convert *.ac to *.3d and even if FlightGear models are generally free, it is not a very good idea to become a "FlightGear models serial converter". FlightGear models -especially flying ones- can be very detailed, generally too much detailed for Sar2. A better way is to open and simplify them as much as possible (I use mainly Blender and / or sometimes Meshlab to do that) BEFORE convert them to *.3d format. - V3dconverter will only do for you the longest and less interesting job, i.e. convert 3D primitives. To get clean Sar2 models, you will certainly have to manually add/remove/modify some stuffs in your *.3d file, and you always will have to convert texture files. * CONVERSION TIPS: @@ -34,12 +34,13 @@ - If your *.3d model seems to have all faces inverted (for example, if when you look at your model from front of it, you see its back texture), you can try "-if" option to invert faces visibility (flip winding). - If you have removed all pure black colors and it already seems that some parts of you model are missing, maybe that missing faces have to be flipped. This can be checked with Blender by showing face orientation. Select bad meshes then use Mesh -> Normals -> Flip to flip them. - Especially if you wants to create new aircrafts, don't hesitate to save your *.obj or *.ac file into small parts, then convert these parts to *.3d, then manually reassemble your multiple *.3d files in the final one. If not, it can be very difficult to define aircraft moving parts (begin_model ... / end_model ...) as rotors, gears, doors, flaps, an so on... + - If you want to understand "how aircraft moving parts work", it can be usefull to convert an existing *.3d aircraft file to *.obj using the v3dconverter '-do-not-transform' option: moving parts will be drawn "as sar2 see them when it reads the *.3d file". - To check your model rendering in Sar2 without add it in a scenery, copy or link your *.3d file in data/aircrafts/ directory, then start Sar2 (aircrafts list is loaded at startup), go to Free Flight, click on Aircraft button, select your model in list, then click on Details button. If your model is not a flying one, don't forget to remove your file / link from aircrafts directory once checked. - Sometimes, v3dconverter prints a lot of (too much ?) warnings. These warnings can be redirected in a text file as this: v3dconverter -i "my input file.obj" -o my_output_file.3d 2> warnings.txt . - If in Blender, your imported *.obj model has no texture and appears pink, it's because Blender can't find the path of textures. This can be fixed by creating a link to the textures folder (for example: "cd my_working_directory && ln -s /usr/share/sar2/textures/"), or by copying the whole textures folder in your working directory, or by editing your model *.mtl file (look for "map_Kd ..........." in *.mtl file). - A Sar2 height field (*.hf) is a rebadged *.tga (Truevision Targa), 8 bits grey color, top left origin, without RLE (Run Length Encoding) compression image. - Keep in mind that *.3d terrain to *.obj conversion -> manual terrain editing -> *.obj to *.hf conversion, is only available for altitude ("Z axis") modification. X and/or Y edition will cause *.obj to *.hf conversion to stop. - - Keep in mind that v3dconverter uses *.3d file data to convert terrain to *.obj, thus it will place your *.obj terrain "as in Sar2". It is very usefull if your terrain is made from multiple tiles, because once each terrain tile converted to *.obj, you can import all of them in Blender and they will automatically be put at the right place, but, if you import only one tile and if translation in *.3d file is not null, terrain can be far away from view center (0,0,0). If you want that it be centered in Blender view, DO NOT move it from Blender, add a -tr (translate) option to your conversion command line. If you move it from Blender, *.obj to *.hf conversion will fail. - - If you want to edit a terrain file in Blender, it's a good idea to add a -sc 1/100 (i.e. scale 1/100) or -sc 1/1000 option to your v3dconverter command line. If not and if -like me- you are not a Blender professional, it will be difficult to see your terrain because it will be too big for camera clipping and focal length. Don't worry about scale value: it will be stored during *.3d to *.obj conversion process, then automatically canceled during *.obj to *.hf conversion. + - Keep in mind that v3dconverter uses *.3d file data to convert terrain to *.obj, thus it will place your *.obj terrain "as in Sar2" (unless you specified the '-do-not-transform' option). It is very usefull if your terrain is made from multiple tiles, because once each terrain tile converted to *.obj, you can import all of them in Blender and they will automatically be put at the right place, but, if you import only one tile and if translation in *.3d file is not null, terrain can be far away from view center (0,0,0). If you want that it be centered in Blender view, DO NOT move it from Blender, add a -tr (translate) option to your conversion command line, or use the '-do-not-transform' option. If you move it from Blender, *.obj to *.hf conversion will fail. + - If you want to edit a terrain file in Blender, it's a good idea to add a -sc 1/100 (i.e. scale 1/100) or -sc 1/1000 option to your v3dconverter command line. If not and if -like me- you are not a Blender professional, it will be difficult to see your terrain because it will be too big for camera clipping and focal length. Don't worry about scale value: it will be stored during *.3d to *.obj conversion process, then automatically canceled during *.obj to *.hf conversion. - Warning: in *.obj terrain files, material has a strange name: this name don't be modified because it will be used as a file name to retrieve important data during *.obj to *.hf conversion process. If you're curious, have a look in $HOME/.local/share/v3dconverter. - You can easily compare two *.hf files with ImageMagick like this: "magick compare image_1.hf.tga image_2.hf.tga -compose src difference.png". diff --git a/tools/v3dconverter/src/objtohf.c b/tools/v3dconverter/src/objtohf.c index fd391b7..7ade0ee 100644 --- a/tools/v3dconverter/src/objtohf.c +++ b/tools/v3dconverter/src/objtohf.c @@ -19,12 +19,10 @@ #include #include - #define FREE_AND_CLOSE_ALL_OBJTOHF \ if ( fpObjIn && (fpObjIn != NULL) ) fclose( fpObjIn ); \ if ( fpHfOut && (fpHfOut != NULL) ) fclose( fpHfOut ); \ -if ( fpConversionDataFileIn != NULL ) fclose( fpConversionDataFileIn ); - +if ( fpConversionDataFileIn != NULL ) fclose( fpConversionDataFileIn );\ int objToHf(const char *source, const char *dest, UserModifier *userModifier ) { FILE *fpObjIn = NULL, *fpHfOut = NULL, *fopen(), *fpConversionDataFileIn = NULL; @@ -33,7 +31,7 @@ int objToHf(const char *source, const char *dest, UserModifier *userModifier ) { unsigned long verticesNr = 0; bool specFileFound = false; double newObjMaxAltitude = 0; // max altitude in modified *.obj file - + struct TGAImageHeader { /*** Fixed length data ***/ uint8_t idFieldLength; // image ID field length @@ -56,11 +54,11 @@ int objToHf(const char *source, const char *dest, UserModifier *userModifier ) { uint8_t heightH; uint8_t bitsPerPixel; uint8_t imageDescriptor; - + /*** Variable length data ***/ // ... }; - + struct TerrainSpec { double sizeX; // "width", in meters double sizeY; // "height", in meters @@ -76,25 +74,25 @@ int objToHf(const char *source, const char *dest, UserModifier *userModifier ) { uint16_t bitsHeight; // image height, in bits (0 to 65535) }; struct TerrainSpec terrainSpec; - + /* * Input and output files ready ? */ if ( ( fpObjIn = fopen(source, "r" ) ) == NULL ) { perror(source); - + FREE_AND_CLOSE_ALL_OBJTOHF return -1; } if ( ( fpHfOut = fopen( dest, "w" ) ) == NULL ) { perror( dest ); - + FREE_AND_CLOSE_ALL_OBJTOHF return -1; } - - - /* + + + /* * Look for material name in *.obj file in order to retrieve terrain specification file name. * This specification file has been automatically generated during *.3d terrain to *.obj conversion * and contains some specification data. Then, extract data from this file. @@ -107,7 +105,7 @@ int objToHf(const char *source, const char *dest, UserModifier *userModifier ) { lineNr++; continue; } - + if ( !strcmp( objTag, "usemtl" ) ) // *.obj material name { if ( sscanf( lineBuffer, "usemtl %s", objTag ) == 1 ) @@ -116,8 +114,8 @@ int objToHf(const char *source, const char *dest, UserModifier *userModifier ) { char v3dConverterPath[] = "/.local/share/v3dconverter/"; unsigned int dirLength = strlen( v3dConverterPath ); int materialNameLength = strlen( objTag ); - - char *name = malloc( homeLength + dirLength + materialNameLength + 1 ); + + char *name = calloc( homeLength + dirLength + materialNameLength + 1, sizeof(char) ); if ( name == NULL ) { fprintf( stderr, "objtohf.c: can't allocate memory for name.\n" ); @@ -125,7 +123,7 @@ int objToHf(const char *source, const char *dest, UserModifier *userModifier ) { } objTag[ 14 ] = '\0'; // limit objTag string length because sometimes Blender adds its own extension to material name. sprintf( name, "%s%s%s", getenv("HOME"), v3dConverterPath, objTag ); - + if ( ( fpConversionDataFileIn = fopen(name, "r" ) ) == NULL ) { specFileFound = false; @@ -133,19 +131,19 @@ int objToHf(const char *source, const char *dest, UserModifier *userModifier ) { else { specFileFound = true; - + while ( fgets( lineBuffer, MAX_LENGTH, fpConversionDataFileIn ) ) { if ( lineBuffer[ 0 ] == '\n' || lineBuffer[ 0 ] == '\r' || ( sscanf( lineBuffer, " %s[\n]", objTag ) ) == 0 ) // blank line or objTag == "" continue; - + if ( lineBuffer[ 0 ] == '#' ) continue; - + int cnt = 0; while ( lineBuffer[ cnt++ ] != '=' ); lineBuffer[ cnt - 1 ] = '\0'; - + if ( !strcmp( lineBuffer, "file" ) ) { strncpy( terrainFileName, &lineBuffer[ cnt ], MAX_LENGTH + 1 ); @@ -217,7 +215,7 @@ int objToHf(const char *source, const char *dest, UserModifier *userModifier ) { fprintf( stdout, "objtohf.c: unexpected data found in file '%s'.\n", name ); } } - free( fpConversionDataFileIn ); + fclose( fpConversionDataFileIn ); fpConversionDataFileIn = NULL; } free( name ); @@ -243,7 +241,7 @@ int objToHf(const char *source, const char *dest, UserModifier *userModifier ) { } lineNr++; } - + /* * Check if all terrain specification values are set, and ask them to user if not. */ @@ -262,22 +260,22 @@ for ( int cnt1 = cnt - 1; cnt1 >= 0; cnt1-- ) \ break; \ }; \ } - + if ( !specFileFound ) { double userAnswer; char c, lineBuffer[MAX_LENGTH + 1] = { '\0' }; int cnt = 0; - + fprintf( stderr, "Terrain specification file not found. Please enter data manually:\n" ); fprintf( stderr, "----------------------------------------------------------------\n" ); - + while ( true ) { fprintf( stderr, "Please enter terrain X value (*.hf width) in meters: " ); - + READ_AND_CHECK_STDIN - + if ( ( sscanf( lineBuffer, "%lf", &userAnswer ) ) == 1 ) { terrainSpec.sizeX = userAnswer; @@ -287,13 +285,13 @@ for ( int cnt1 = cnt - 1; cnt1 >= 0; cnt1-- ) \ else fprintf( stderr, "INPUT ERROR\n" ); } - + while ( true ) { fprintf( stderr, "Please enter terrain Y (*.hf height) value in meters: " ); - + READ_AND_CHECK_STDIN - + if ( ( sscanf( lineBuffer, "%lf", &userAnswer ) ) == 1 ) { terrainSpec.sizeY = userAnswer; @@ -303,13 +301,13 @@ for ( int cnt1 = cnt - 1; cnt1 >= 0; cnt1-- ) \ else fprintf( stderr, "INPUT ERROR\n" ); } - + while ( true ) { fprintf( stderr, "Please enter terrain Z value (*.hf max altitude) in meters: " ); - + READ_AND_CHECK_STDIN - + if ( ( sscanf( lineBuffer, "%lf", &userAnswer ) ) == 1 ) { terrainSpec.maxAltitude = userAnswer; @@ -319,13 +317,13 @@ for ( int cnt1 = cnt - 1; cnt1 >= 0; cnt1-- ) \ else fprintf( stderr, "INPUT ERROR\n" ); } - + while ( true ) { fprintf( stderr, "Please enter terrain translation X value in meters: " ); - + READ_AND_CHECK_STDIN - + if ( ( sscanf( lineBuffer, "%lf", &userAnswer ) ) == 1 ) { terrainSpec.xPos = userAnswer; @@ -335,13 +333,13 @@ for ( int cnt1 = cnt - 1; cnt1 >= 0; cnt1-- ) \ else fprintf( stderr, "INPUT ERROR\n" ); } - + while ( true ) { fprintf( stderr, "Please enter terrain translation Y value in meters: " ); - + READ_AND_CHECK_STDIN - + if ( ( sscanf( lineBuffer, "%lf", &userAnswer ) ) == 1 ) { terrainSpec.yPos = userAnswer; @@ -351,13 +349,13 @@ for ( int cnt1 = cnt - 1; cnt1 >= 0; cnt1-- ) \ else fprintf( stderr, "INPUT ERROR\n" ); } - + while ( true ) { fprintf( stderr, "Please enter terrain translation Z value in meters: " ); - + READ_AND_CHECK_STDIN - + if ( ( sscanf( lineBuffer, "%lf", &userAnswer ) ) == 1 ) { terrainSpec.zPos = userAnswer; @@ -367,13 +365,13 @@ for ( int cnt1 = cnt - 1; cnt1 >= 0; cnt1-- ) \ else fprintf( stderr, "INPUT ERROR\n" ); } - + while ( true ) { fprintf( stderr, "Please enter *.obj terrain scale value (\"*.obj/*.hf distances ratio\"): " ); - + READ_AND_CHECK_STDIN - + if ( ( sscanf( lineBuffer, "%lf", &userAnswer ) ) == 1 ) { terrainSpec.scale = userAnswer; @@ -383,13 +381,13 @@ for ( int cnt1 = cnt - 1; cnt1 >= 0; cnt1-- ) \ else fprintf( stderr, "INPUT ERROR\n" ); } - + while ( true ) { fprintf( stderr, "Please enter *.hf texture width number of bits: " ); - + READ_AND_CHECK_STDIN - + if ( ( sscanf( lineBuffer, "%lf", &userAnswer ) ) == 1 ) { terrainSpec.bitsWidth = userAnswer; @@ -399,13 +397,13 @@ for ( int cnt1 = cnt - 1; cnt1 >= 0; cnt1-- ) \ else fprintf( stderr, "INPUT ERROR\n" ); } - + while ( true ) { fprintf( stderr, "Please enter *.hf texture height number of bits: " ); - + READ_AND_CHECK_STDIN - + if ( ( sscanf( lineBuffer, "%lf", &userAnswer ) ) == 1 ) { terrainSpec.bitsHeight = userAnswer; @@ -415,11 +413,11 @@ for ( int cnt1 = cnt - 1; cnt1 >= 0; cnt1-- ) \ else fprintf( stderr, "INPUT ERROR\n" ); } - - + + if ( valueSet != TERRAIN_SPEC_VALUES_TO_SET ) fprintf( stderr, "objtohf.c : something bad has happen. Wrong TERRAIN_SPEC_VALUES_TO_SET ?\n" ); - + /* fprintf( stderr, " terrain width = %lf\n", terrainSpec.sizeX); fprintf( stderr, "terrain height = %lf\n", terrainSpec.sizeY); @@ -432,8 +430,8 @@ for ( int cnt1 = cnt - 1; cnt1 >= 0; cnt1-- ) \ fprintf( stderr, " terrain bitsH = %d\n", terrainSpec.bitsHeight); */ } - - + + /* * Prepare tga '.hf' image header */ @@ -455,33 +453,33 @@ for ( int cnt1 = cnt - 1; cnt1 >= 0; cnt1-- ) \ hfTexHeader.heightL = terrainSpec.bitsHeight & 0xFF; hfTexHeader.heightH = terrainSpec.bitsHeight >> 8; hfTexHeader.bitsPerPixel = 8; - hfTexHeader.imageDescriptor = 32; // Top left origin - - + hfTexHeader.imageDescriptor = 0; + hfTexHeader.imageDescriptor |= TGA_TOP_LEFT_MASK; + /* - * Reserve memory for tga '.hf' image data (i.e. color bytes). + * Reserve memory for tga '.hf' image data (i.e. gray bytes). */ uint64_t imageDataBytes = ( 256 * hfTexHeader.widthH + hfTexHeader.widthL ) * ( 256 * hfTexHeader.heightH + hfTexHeader.heightL ) * ( hfTexHeader.bitsPerPixel / 8 ); uint8_t *tgaImageData = malloc( imageDataBytes ); if ( tgaImageData == NULL ) { fprintf( stderr, "Can't allocate memory for *.hf TGA image data.\n" ); - + FREE_AND_CLOSE_ALL_OBJTOHF return -1; } - - + + /* * Read and process each *.obj file line in order to extract vertices, * then convert vertices coordinates to tga ".hf" image column bit number, row bit number, and grey value (i.e. altitude, from 0 to 255). */ rewind( fpObjIn ); lineNr = 1; - double perPixelXDistance = terrainSpec.sizeX / (double)terrainSpec.bitsWidth; - double perPixelYDistance = terrainSpec.sizeY / (double)terrainSpec.bitsHeight; + double perPixelXDistance = terrainSpec.sizeX / (double)(terrainSpec.bitsWidth - 1); + double perPixelYDistance = terrainSpec.sizeY / (double)(terrainSpec.bitsHeight - 1); double newTerrainMaxAltitude = ( newObjMaxAltitude - terrainSpec.zPos ) / terrainSpec.scale; - + double perPixelZDistance = 0; if ( round( newTerrainMaxAltitude ) <= round( terrainSpec.maxAltitude ) ) perPixelZDistance = terrainSpec.maxAltitude / 255; // altitude is always an 8 bits value @@ -491,9 +489,9 @@ for ( int cnt1 = cnt - 1; cnt1 >= 0; cnt1-- ) \ fprintf( stdout, "\e[1;33mYou should modify the heightfield_load 4th argument (altitude) value to %.3f in the *.3d terrain file\e[0m.\n", newTerrainMaxAltitude ); //fprintf( stdout, "\e[1;33mYou should modify the heightfield_load 4th argument (altitude) value to %.3f instead of %.3f in the %s file\e[0m.\n", newTerrainMaxAltitude, terrainSpec.maxAltitude, terrainFileName ); } - - double halfXSize = terrainSpec.sizeX / 2 - perPixelXDistance / 2; - double halfYSize = terrainSpec.sizeY / 2 - perPixelYDistance / 2; + + double halfXSize = terrainSpec.sizeX / 2; + double halfYSize = terrainSpec.sizeY / 2; double x = 0; double y = 0; double z = 0; @@ -504,53 +502,53 @@ for ( int cnt1 = cnt - 1; cnt1 >= 0; cnt1-- ) \ lineNr++; continue; } - + if ( !strcmp( objTag, "v" ) ) // vertex { verticesNr++; - + sscanf( lineBuffer, "%*s %lf %lf %lf", &x, &z, &y ); y = -y; - + /* Revert *.3d to *.obj scale (set by user) */ x /= terrainSpec.scale ; y /= terrainSpec.scale ; z /= terrainSpec.scale ; - + /* Revert translations. These translations includes *.3d terrain translations and translations requested by user. */ x -= terrainSpec.xPos; y -= terrainSpec.yPos; z -= terrainSpec.zPos; - + double col = ( x + halfXSize ) / perPixelXDistance; - double row = (double)(terrainSpec.bitsWidth) - 1 - ( y + halfYSize ) / perPixelYDistance; + double row = ( y + halfYSize ) / perPixelYDistance; double altitude = z / perPixelZDistance; - + col = round(col); row = round(row); altitude = round(altitude); - + if ( altitude > 255 ) altitude = 255; else if ( altitude < 0 ) altitude = 0; - + if ( col < 0 || col > terrainSpec.bitsWidth - 1 ) { - fprintf( stderr, "Error : Bad column number detected in *.hf image (col = %lf, row = %lf). Exiting.\n", col, row ); - + fprintf( stderr, "Error : Bad column number detected in *.hf image (col = %d, row = %d). Exiting.\n", (int)col, (int)row ); + FREE_AND_CLOSE_ALL_OBJTOHF return -1; } - + if ( row < 0 || row > terrainSpec.bitsHeight - 1 ) { - fprintf( stderr, "Error : Bad row number detected in *.hf image (col = %lf, row = %lf). Exiting.\n", col, row ); - + fprintf( stderr, "Error : Bad row number detected in *.hf image (col = %d, row = %d). Exiting.\n", (int)col, (int)row ); + FREE_AND_CLOSE_ALL_OBJTOHF return -1; } - + unsigned long offset = (unsigned long)col + (unsigned long)row * terrainSpec.bitsWidth; if ( ( offset >= 0 ) && ( offset < imageDataBytes ) ) { @@ -560,7 +558,7 @@ for ( int cnt1 = cnt - 1; cnt1 >= 0; cnt1-- ) \ { fprintf( stderr, "objtohf.c : tgaImageData offset error. Bad terrain specification data ?\n" ); FREE_AND_CLOSE_ALL_OBJTOHF - + return EXIT_FAILURE; } } @@ -571,33 +569,41 @@ for ( int cnt1 = cnt - 1; cnt1 >= 0; cnt1-- ) \ { fprintf( stderr, "File %s, line #%ld: can't convert *.obj to *.hf because more than one object found in *.obj file. Exiting.\n", source, lineNr ); FREE_AND_CLOSE_ALL_OBJTOHF - + return EXIT_FAILURE; } } lineNr++; } - - + + /* * Write tga ".hf" image header */ fwrite( &hfTexHeader, sizeof( hfTexHeader ), 1, fpHfOut ); - + /* * No color map to write */ - + /* - * Write image data + * Write image data. */ - for ( unsigned long cnt0 = 0; cnt0 < imageDataBytes / terrainSpec.bitsWidth; cnt0++ ) + + if ( ( hfTexHeader.imageDescriptor & 0b00110000 ) == TGA_TOP_LEFT_MASK ) { - fwrite( &tgaImageData[ cnt0 * terrainSpec.bitsWidth ], terrainSpec.bitsWidth, 1, fpHfOut ); + /* A 'default' (i.e. with image descriptor bits 5 and 4 = 00) tga image + * is bottom left origin: as we want to produce a top left origin + * image, rows order must be flipped. + */ + for ( long row = terrainSpec.bitsHeight - 1 ; row >= 0 ; row-- ) + { + fwrite( &tgaImageData[ row * terrainSpec.bitsWidth ], terrainSpec.bitsWidth, 1, fpHfOut ); + } } - + FREE_AND_CLOSE_ALL_OBJTOHF - + return EXIT_SUCCESS; } diff --git a/tools/v3dconverter/src/objtov3d.c b/tools/v3dconverter/src/objtov3d.c index f1937f9..8fbf361 100644 --- a/tools/v3dconverter/src/objtov3d.c +++ b/tools/v3dconverter/src/objtov3d.c @@ -63,7 +63,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) double XYZradius, maxXYZradius = 0; // object spherical bound (maxXYZradius) char lineBuffer[MAX_LENGTH + 1], objTag[MAX_LENGTH + 1]; char previousV3dParam[ 40 + 1 ] = ""; - + #define V3DMODELTYPES 18+1 #define V3DMODELTYPEMAXLENGTH 14 char v3dModelType[ V3DMODELTYPES ][ V3DMODELTYPEMAXLENGTH + 1 ] = { @@ -88,7 +88,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) { "" } }; char currentV3dModelType[ V3DMODELTYPEMAXLENGTH + 1 ] = ""; - + /* Input and output files ok ? */ if ( ( fpIn = fopen(source, "r" ) ) == NULL ) { perror(source); @@ -96,10 +96,11 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) } if ( ( fpOut = fopen( dest, "w+" ) ) == NULL ) { fclose( fpIn ); + fpIn = NULL; perror( dest ); return -1; } - + /* Prepare structures */ Vcoord *v = malloc ( sizeof ( Vcoord ) ); if ( v == NULL ) @@ -107,7 +108,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) fprintf( stderr, "Can't allocate memory for Vcoord.\n"); return EXIT_FAILURE; } - + Vnormal *vn = malloc ( sizeof ( Vnormal ) ); if ( vn == NULL ) { @@ -115,7 +116,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) free( v ); return EXIT_FAILURE; } - + Tcoord *vt = malloc ( sizeof ( Tcoord ) ); if ( vt == NULL ) { @@ -136,7 +137,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) } struct Color currentColor = { -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0 }; - + /* Read and process each *.obj file line */ while ( fgets(lineBuffer, MAX_LENGTH, fpIn) ) { if ( lineBuffer[ 0 ] == '\n' || lineBuffer[ 0 ] == '\r' || ( sscanf( lineBuffer, " %s[\n]", objTag ) ) == 0 ) // blank line or objTag == "" @@ -151,9 +152,9 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) fprintf( fpOut, "end_%s\n", previousV3dParam ); // write end of previous parameter type (if any) strcpy( previousV3dParam, "" ); } - + vertices++; // add a vertex coord. v[0] is never used (as in *.obj file format description). - + Vcoord *tmp = realloc( v, ( vertices + 1 ) * sizeof( Vcoord ) ); if (tmp == NULL) { @@ -168,7 +169,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) double foo = 0; // rotateZ waits for a 'w' value RotateX ( &vx, &vy, &vz, &foo, -90 ); // rotate vertex arround X axis. Angle given in degrees. RotateZ ( &vx, &vy, &vz, &foo, 90 ); // rotate vertex arround Z axis. Angle given in degrees. - + /* apply user modifiers. Apply order is: X rotation, then Y rotation, then Z rotation, then scale, then translations. */ if ( userModifier->rx != 0.0 ) { @@ -182,14 +183,14 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) { RotateZ ( &vx, &vy, &vz, &foo, userModifier->rz ); } - + if ( userModifier->scale != 1.0 ) { vx *= userModifier->scale; vy *= userModifier->scale; vz *= userModifier->scale; } - + if ( userModifier->tx != 0.0 ) { vx += userModifier->tx; @@ -202,12 +203,12 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) { vz += userModifier->tz; } - + /* store coords */ v[vertices].x = vx; v[vertices].y = vy; v[vertices].z = vz; - + /* easy way to compute object rectangular bounds */ if ( vx < Xmin ) Xmin = vx; @@ -221,7 +222,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) Zmin = vz; if ( vz > Zmax ) Zmax = vz; - + /* cylindrical bounds */ XYradius = sqrt( pow( vx, 2 ) + pow( vy, 2 ) ); if ( XYradius > maxXYradius ) @@ -239,9 +240,9 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) fprintf( fpOut, "end_%s\n", previousV3dParam ); // write end of previous parameter type (if any) strcpy( previousV3dParam, "" ); } - + normals++; // add a vertex normal. vn[0] is not used. - + Vnormal *tmp = realloc( vn, ( normals + 1 ) * sizeof( Vnormal ) ); if (tmp == NULL) { @@ -250,15 +251,15 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) return EXIT_FAILURE; } vn = tmp; - + /* read vertex normal coordinates and re-orient them because obj coords system is different from v3d coords system */ sscanf( lineBuffer, " vn %lf %lf %lf", &vni, &vnj, &vnk ); double foo = 0; // rotateZ waits for a 'w' value RotateX ( &vni, &vnj, &vnk, &foo, -90 ); // rotate vertex arround X axis. Angle given in degrees. RotateZ ( &vni, &vnj, &vnk, &foo, 90 ); // rotate vertex arround Z axis. Angle given in degrees. - + /* it's a normal, no need to apply user modifiers. */ - + /* store coords */ vn[normals].i = vni; vn[normals].j = vnj; @@ -271,9 +272,9 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) fprintf( fpOut, "end_%s\n", previousV3dParam ); // write end of previous parameter type (if any) strcpy( previousV3dParam, "" ); } - + textCoords++; // add a vertex texture coord. vt[0] is not used. - + Tcoord *tmp = realloc( vt, ( textCoords + 1 ) * sizeof( Tcoord ) ); if (tmp == NULL) { @@ -282,7 +283,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) return EXIT_FAILURE; } vt = tmp; - + sscanf( lineBuffer, " vt %lf %lf %lf", &vt[textCoords].u, &vt[textCoords].v, &vt[textCoords].w ); } else if ( !strcmp( objTag, "p" ) ) // point @@ -293,10 +294,10 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) fprintf( fpOut, "end_%s\n", previousV3dParam ); // write end of previous parameter type (if any) else fprintf( fpOut, "begin_points\n" ); - + strcpy( previousV3dParam, "points" ); } - + const char delim[2] = " "; char *token; int vNum = 0; @@ -304,7 +305,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) token = strtok(NULL, delim); // second token contains first point vertex while( token != NULL ) { sscanf( token, " %d", &vNum ); // read vertex number - + /* write point coord * Info: an obj point don't have a normal, but a v3d point seems to have one (which is always 0.0 0.0 1.0 ?). * FIXME: is it necessary to add a normal, and if yes, how to compute this normal? This point is not on a surface! @@ -322,7 +323,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) int vNum = 0, vtNum = 0; const char delim[2] = " "; char *token; - + for ( int cnt = 0; cnt < strlen( lineBuffer ); cnt++ ) { if ( lineBuffer[ cnt ] == ' ' || lineBuffer[ cnt ] == '\t' ) { @@ -331,20 +332,20 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) lastStatementPos = cnt - 1; } } - + char lastToken[MAX_LENGTH + 1]; for ( int cnt = lastStatementPos; lineBuffer[ cnt ] != '\0'; cnt++ ) lastToken[ cnt - lastStatementPos ] = lineBuffer[ cnt + 1 ]; sscanf( lastToken, " %d", &lastVertexNr ); sscanf( lineBuffer, " l %d", &firstVertexNr ); - + if ( lineVerticies == 2 ) // it's a single line { if ( strcmp( previousV3dParam, "lines" ) ) // if previous type was not "lines" { fprintf( fpOut, "end_%s\n", previousV3dParam ); // write end of previous parameter type (if any) fprintf( fpOut, "begin_lines\n" ); - + strcpy( previousV3dParam, "lines" ); } } @@ -353,30 +354,30 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) // always only one loop per v3d "begin_line_loop" statement: no need to check previous type, just "end_" it. if ( previousV3dParam[0] != '\0' ) fprintf( fpOut, "end_%s\n", previousV3dParam ); // write end of previous parameter type (if any) - + fprintf( fpOut, "begin_line_loop\n" ); strcpy( previousV3dParam, "line_loop" ); lineVerticies--; // will be used when writing datas to avoid last vertex print in case of line loop - + } else // there are more than 2 vertices, and it's a line strip { // always only one strip per v3d "begin_line_strip" statement: no need to check previous type, just "end_" it. if ( previousV3dParam[0] != '\0' ) fprintf( fpOut, "end_%s\n", previousV3dParam ); // write end of previous parameter type (if any) - + fprintf( fpOut, "begin_line_strip\n" ); strcpy( previousV3dParam, "line_strip" ); } - + // write datas token = strtok( lineBuffer, delim ); // first token contains "l" (line) token = strtok( NULL, delim ); // second token contains first v/vt reference numbers while( token != NULL ) { vNum = 0; vtNum = 0; sscanf( token, " %d/%d", &vNum, &vtNum ); - + if ( lineVerticies > 0 ) // check lineVerticies value to avoid last vertex print in case of line loop { if ( textureOn == true ) @@ -387,14 +388,14 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) else if ( vtNum < 0 ) fprintf( fpOut, " texcoord %f %f\n", vt[textCoords + vtNum].u, vt[textCoords + vtNum].v ); } - + // write vertex coord if ( vNum > 0 ) fprintf( fpOut, " %f %f %f\n", v[vNum].x, v[vNum].y, v[vNum].z ); else fprintf( fpOut, " %f %f %f\n", v[vertices + vNum].x, v[vertices + vNum].y, v[vertices + vNum].z ); } - + lineVerticies--; token = strtok( NULL, delim ); // next token } @@ -404,7 +405,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) char *token; long vNum = 0, vtNum = 0, vnNum = 0; int faceVerticies = 0; - + faces++; /* count face vertices in order to determine face type (triangle, quad, polygon) */ @@ -422,14 +423,14 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) if ( cnt < bufLength ) faceVerticies++; } - + if ( faceVerticies == 3 ) // triangle { if ( strcmp( previousV3dParam, "triangles" ) ) // previous surface type was not triangles ? { if ( previousV3dParam[0] != '\0' ) fprintf( fpOut, "end_%s\n", previousV3dParam ); // write end of previous parameter type (if any) - + fprintf( fpOut, "begin_triangles\n" ); strcpy( previousV3dParam, "triangles" ); } @@ -440,7 +441,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) { if ( previousV3dParam[0] != '\0' ) fprintf( fpOut, "end_%s\n", previousV3dParam ); // write end of previous parameter type (if any) - + fprintf( fpOut, "begin_quads\n" ); strcpy( previousV3dParam, "quads" ); } @@ -451,14 +452,14 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) if ( previousV3dParam[0] != '\0' ) fprintf( fpOut, "end_%s\n", previousV3dParam ); // write end of previous parameter type (if any) fprintf( fpOut, "begin_polygon\n" ); - + strcpy( previousV3dParam, "polygon" ); } else // faceVerticies < 3 : should never happen !!! { fprintf( stderr, " Warning in *.obj file, line #%ld: face with only %d vertices detected and removed.\n", lineNr, faceVerticies ); } - + /* alloc memory */ Vertex *tmpVertex = calloc ( faceVerticies, sizeof ( Vertex ) ); if ( tmpVertex == NULL ) @@ -467,7 +468,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) FREE_AND_CLOSE_ALL_OBJTOV3D return EXIT_FAILURE; } - + Vcoord *tmpVertex2 = calloc ( faceVerticies, sizeof ( Vcoord ) ); // for surface normal calculation (when necessary) if ( tmpVertex2 == NULL ) { @@ -475,25 +476,25 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) FREE_AND_CLOSE_ALL_OBJTOV3D return EXIT_FAILURE; } - + /* face type (triangle, quad, polygon) has been determined. Now, read input line until end and write face vertices. */ token = strtok( lineBuffer, " " ); // first token contains "f" (face) token = strtok( NULL, " " ); // second token contains first v/vt/vn reference numbers long tabCounter; double first_i, first_j, first_k; bool faceNormal = true; - + if ( userModifier->invertFaces == 0 ) // default case: faces MUST be re-oriented { tabCounter = faceVerticies; - + while( token != NULL ) { tabCounter--; - + if ( !( sscanf( token, " */%ld/*", &tmpVertex[tabCounter].vt ) ) ) // 'v' / no 'vt' / 'vn' case : sscanf will stop reading after 'v' sscanf( token, " %ld//%ld", &tmpVertex[tabCounter].v, &tmpVertex[tabCounter].vn ); sscanf( token, " %ld/%ld/%ld", &tmpVertex[tabCounter].v, &tmpVertex[tabCounter].vt, &tmpVertex[tabCounter].vn ); - + token = strtok( NULL, " " ); // next token } @@ -511,9 +512,9 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) if ( !( sscanf( token, " */%ld/*", &tmpVertex[tabCounter].vt ) ) ) // 'v' / no 'vt' / 'vn' case : sscanf will stop reading after 'v' sscanf( token, " %ld//%ld", &tmpVertex[tabCounter].v, &tmpVertex[tabCounter].vn ); // read v and vn sscanf( token, " %ld/%ld/%ld", &tmpVertex[tabCounter].v, &tmpVertex[tabCounter].vt, &tmpVertex[tabCounter].vn ); // read v, vt, and vn if any - + token = strtok( NULL, " " ); // next token - + tabCounter++; } for ( int cnt0 = 0; cnt0 < faceVerticies; cnt0++ ) @@ -523,7 +524,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) tmpVertex2[ cnt0 ].z = v[ faceVerticies - cnt0 ].z; } } - + /* check faces has identical vertices */ bool faceHasIdenticalVertexIndicies = false; for ( int cnt0 = 0; cnt0 < faceVerticies - 1; cnt0++ ) @@ -538,7 +539,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) if ( faceHasIdenticalVertexIndicies == true ) break; } - + if ( faceHasIdenticalVertexIndicies == true ) { fprintf( stderr, " Warning in *.obj file, line #%ld: identical vertex indicies found in the same face. Face ignored.\n", lineNr ); @@ -557,7 +558,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) ; // DIV BY ZERO ERROR when calculating normal } else - { // check if normal is a face normal (i.e. not a vertex one). If all vertices on a face have the same normal, then normal is a face normal. + { // check if normal is a face normal (i.e. not a vertex one). If all vertices on a face have the same normal, then normal is a face normal. vnNum = tmpVertex[0].vn; if ( vnNum > 0 ) { @@ -571,13 +572,13 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) first_j = vn[normals + vnNum].j; first_k = vn[normals + vnNum].k; } - + for ( tabCounter = 1; tabCounter < faceVerticies; tabCounter++ ) { vnNum = tmpVertex[tabCounter].vn; if ( vnNum > 0 ) { - if ( ( vn[vnNum].i != first_i ) || ( vn[vnNum].j != first_j ) || ( vn[vnNum].k != first_k ) ) + if ( ( vn[vnNum].i != first_i ) || ( vn[vnNum].j != first_j ) || ( vn[vnNum].k != first_k ) ) { faceNormal = false; break; @@ -585,7 +586,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) } else // vnNum is negative (a *.obj vertex number can't be null) { - if ( ( vn[normals + vnNum].i != first_i ) || ( vn[normals + vnNum].j != first_j ) || ( vn[normals + vnNum].k != first_k ) ) + if ( ( vn[normals + vnNum].i != first_i ) || ( vn[normals + vnNum].j != first_j ) || ( vn[normals + vnNum].k != first_k ) ) { faceNormal = false; break; @@ -598,7 +599,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) } } free( tmpVertex2 ); - + for ( tabCounter = 0; tabCounter < faceVerticies; tabCounter++ ) { vnNum = tmpVertex[tabCounter].vn; @@ -612,7 +613,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) else // vnNum < 0 fprintf( fpOut, " normal %f %f %f\n", vn[normals + vnNum].i, vn[normals + vnNum].j, vn[normals + vnNum].k ); } - + vtNum = tmpVertex[tabCounter].vt; // write texture coord only if vtNum is not equal to 0 and texture is on. // "textureOn" test is not mandatory because texcoord statement is automatically ignored by v3d if texture is off, @@ -623,7 +624,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) fprintf( fpOut, " texcoord %f %f\n", vt[vtNum].u, vt[vtNum].v ); else // vtNum < 0 fprintf( fpOut, " texcoord %f %f\n", vt[textCoords + vtNum].u, vt[textCoords + vtNum].v ); - + vNum = tmpVertex[tabCounter].v; // write vertex coord if ( vNum > 0 ) @@ -631,7 +632,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) else // vNum < 0 fprintf( fpOut, " %f %f %f\n", v[vertices + vNum].x, v[vertices + vNum].y, v[vertices + vNum].z ); } - + free( tmpVertex ); } else if ( !strcmp( objTag, "mtllib" ) ) // material file @@ -641,7 +642,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) */ char mtlSourceTmp[ MAX_LENGTH + 1 ]; char mtlSource[ 2 * MAX_LENGTH + 2 ]; - + char *path = NULL; char *textureBaseName = NULL; char *modelBaseName = NULL; @@ -663,7 +664,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) for ( originPos = start; originPos < strlen ( lineBuffer ); originPos++ ) tempBuffer[ copyPos++ ] = lineBuffer[ originPos ]; tempBuffer[ copyPos ] = '\0'; - + int mtllibFileStart[MAX_MTLLIB_FILENAMES], mtllibFileNr = 0; mtllibFileStart[mtllibFileNr++] = 0; @@ -691,9 +692,9 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) for ( int cnt1 = mtllibFileStart[ cnt ]; cnt1 < mtllibFileStart[ cnt + 1 ]; cnt1++ ) mtlSourceTmp[ copyPos++ ] = tempBuffer[ cnt1 ]; mtlSourceTmp[ copyPos ] = '\0'; - + sprintf( mtlSource, "%s%s", path, mtlSourceTmp ); // put *.obj file path before mtl file name - + int result = readMtlFile( mtlSource, &mat, &materials ); if ( result == -1 ) { @@ -708,10 +709,10 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) return EXIT_FAILURE; } } - + fprintf( fpOut, "begin_header\n" ); fprintf( fpOut, "creator v3dconverter\n" ); - + if ( materials > 0 ) { if ( path != NULL ) @@ -744,19 +745,19 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) if ( modelBaseName[ cnt ] == ' ') modelBaseName[ cnt ] = '_'; } - + // Use model base name as texture sub-directory name fprintf( fpOut, "texture_load %s textures/%s/", mat[ cnt ].name, modelBaseName ); - + char *fullFileName = strdup( mat[ cnt ].texName ); - + scanFileName( fullFileName, &path, &textureBaseName, &extension ); // scan material texName to extract texture name if ( fullFileName != NULL ) { free( fullFileName ); fullFileName = NULL; } - + // Use texture base name as *.tex texture file name fprintf( fpOut, "%s.tex 0.8\n", textureBaseName ); } @@ -782,16 +783,16 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) extension = NULL; } } - + fprintf( fpOut, "end_header\n\n" ); - + /* Considering that 'mtllib' is the first statement of an *.obj file, once all texture_load lines are written, * we can write some generic statements. */ fprintf( fpOut, "# Generic statements for making this object visible:\n" ); //fprintf( fpOut, "type 1\n" ); // deprecated fprintf( fpOut, "range 2000\n\n" ); - + fprintf( fpOut, "# Generic statement to add object smoothing. Generally, buildings do not need to be smoothed:\n" ); fprintf( fpOut, "shade_model_smooth\n\n" ); } @@ -802,7 +803,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) fprintf( fpOut, "end_%s\n", previousV3dParam ); // write end of previous parameter type (if any) strcpy( previousV3dParam, "" ); } - + char mtlName[MAX_LENGTH + 1]; int cnt0 = 0, cnt1 = 0; while ( lineBuffer[cnt0++] != ' ' ); // discard "usemtl " @@ -815,7 +816,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) mtlName[cnt1++] = lineBuffer[cnt0++]; } mtlName[cnt1] = '\0'; // close string - + int mtlNr = 0; while ( mtlNr < materials ) // look for material number { @@ -833,9 +834,9 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) double new_r = 0.2, new_g = 0.2, new_b = 0.2, new_a = 1.0, new_amb = 1.0, new_dif = 1.0, new_spe = 0.1, new_shi = 0.0, new_emi = 0.1; // init and default values int counter = 0; double KTotal = 0; - + // *** Remember that readMtlFile() initialized unread values to -1.0 *** - + /* Color */ if ( mat[ mtlNr ].KdR >= 0 ) new_r = mat[ mtlNr ].KdR; // use mtl diffuse red color as v3d base color @@ -849,19 +850,19 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) new_b = mat[ mtlNr ].KdB; // use mtl diffuse blue color as v3d base color else ; // use default new_b value - + /* Transparency */ if ( mat[ mtlNr ].d >= 0 ) new_a = mat[ mtlNr ].d; // transparency ( mtl "dissolve" coefficient ) else ; // use default new_a value - + /* Ambient coefficient */ new_amb = 1.0; // FIXME force ambient coefficient to 1.0 (as Blender seems to do). - + /* Diffuse coefficient */ new_dif = 1.0; // mtl Kd diffuse color was used as v3d base color, then Kd(r,g,b) / new_(r,g,b) = 1.0 - + /* Specularity coefficent. FIXME Is this calculation right ??? */ counter = 0; KTotal = 0.0; @@ -884,13 +885,13 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) new_spe = KTotal / counter; else ; // use default new_spe value - + /* Shininess coefficient */ if ( mat[ mtlNr ].Ns >= 0 ) new_shi = mat[ mtlNr ].Ns / 1000; // Ns range is 0 to 1000 else ; // use default value - + /* Emission coefficient. FIXME Is this calculation right ??? */ counter = 0; KTotal = 0.0; @@ -913,14 +914,14 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) new_emi = KTotal / counter; else ; // use default new_emi value - + if ( mat[ mtlNr ].texName != NULL ) // if material is a texture, force color to white { new_r = 1.0; new_g = 1.0; new_b = 1.0; } - + /* check if current color is same as new one */ if ( currentColor.r == new_r && currentColor.g == new_g @@ -947,7 +948,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) currentColor.spe = new_spe; currentColor.shi = new_shi; currentColor.emi = new_emi; - + if ( mat[ mtlNr ].texName == NULL ) // if material has no texture ... { if ( textureOn == true ) // ... and if previous material was a textured one @@ -956,19 +957,19 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) textureOn = false; } } - + fprintf( fpOut, "\n#OBJFILE material: %s\n", mtlName ); fprintf( fpOut, "color %.6f %.6f %.6f", currentColor.r, currentColor.g, currentColor.b ); // v3d base color fprintf( fpOut, " %.6f", currentColor.a ); // transparency fprintf( fpOut, " %.6f %.6f %.6f %.6f %.6f\n", currentColor.amb, currentColor.dif, currentColor.spe, currentColor.shi, currentColor.emi ); // coefficients } - + if ( mat[ mtlNr ].texName != NULL ) // if material is a texture, print it { fprintf( fpOut, "texture_select %s\n", mat[ mtlNr ].name ); textureOn = true; } - + fprintf( fpOut, "\n" ); } } @@ -979,7 +980,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) int cnt = 0; while ( lineBuffer[ cnt ] != '\n' && lineBuffer[ cnt++ ] != '\r' ); lineBuffer[ cnt ] = '\0'; - + fprintf( fpOut, "#OBJFILE group: %s\n", lineBuffer ); } */ @@ -990,7 +991,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) fprintf( fpOut, "end_%s\n", previousV3dParam ); // write end of previous parameter type (if any) strcpy( previousV3dParam, "" ); } - + // Don't convert obj 's' statement into v3d 'shade_model_*' because only one 'shade_model_*' per V3d object. /* int foo = -1; @@ -1017,26 +1018,26 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) fprintf( fpOut, "texture_off\n" ); textureOn = false; } - + /* check if object name is known as a v3d model type */ char objectName[MAX_LENGTH + 1]; int cnt = 0; sscanf( lineBuffer, " %*s %[^\n]", objectName ); // read object name - + while ( cnt < V3DMODELTYPES ) { if ( !strcmp( objectName, v3dModelType[ cnt ] ) ) // if object name found in v3dModelType[] array break; cnt++; } - + if ( cnt != V3DMODELTYPES ) // if object name is a v3d model type { if ( currentV3dModelType[0] != '\0' ) { fprintf( fpOut, "end_model %s\n", currentV3dModelType ); } - + fprintf( fpOut, "begin_model %s\n", objectName ); strcpy( currentV3dModelType, objectName ); } @@ -1051,7 +1052,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) fprintf( fpOut, "#OBJFILE object: %s\n", objectName ); else fprintf( fpOut, "#OBJFILE group: %s\n", objectName ); - + if ( firstObject ) { strcpy( currentV3dModelType, "standard" ); @@ -1066,7 +1067,7 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) int cnt = 0; while ( lineBuffer[ cnt ] != '\n' && lineBuffer[ cnt++ ] != '\r' ); lineBuffer[ cnt ] = '\0'; - + fprintf( fpOut, "#OBJFILE comment: %s\n", lineBuffer ); } else if ( !strcmp( objTag, "curv" ) || !strcmp( objTag, "curv2" ) || !strcmp( objTag, "surf" ) ) @@ -1080,20 +1081,20 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) fprintf( fpOut, "end_%s\n", previousV3dParam ); // write end of previous parameter type (if any) strcpy( previousV3dParam, "" ); } - + fprintf( stderr, " Warning in *.obj file, line #%ld: unknown tag \"%s\".\n", lineNr, objTag ); } lineNr++; - + } // end of main file read/write loop - + if ( previousV3dParam[0] != '\0' ) fprintf( fpOut, "end_%s\n", previousV3dParam ); // write end of previous parameter type (triangle, quad, line, ...), if any. - + if ( currentV3dModelType[0] != '\0' ) fprintf( fpOut, "end_model %s\n", currentV3dModelType ); // write end of previous model type (if any) - + // print some usefull information at start of v3d output file and on console FILE* tmp = tmpfile(); int c; @@ -1116,11 +1117,12 @@ int objToV3d(const char *source, const char *dest, UserModifier *userModifier ) while ( ( c = fgetc( tmp ) ) != EOF ) fputc( c, fpOut ); fclose( tmp ); + tmp = NULL; //fprintf( stderr, "Number of\n vertices: %ld\n normals: %ld\n textCoords: %ld\n faces: %ld\n", vertices, normals, textCoords, faces ); - + FREE_AND_CLOSE_ALL_OBJTOV3D - + return EXIT_SUCCESS; } @@ -1130,12 +1132,12 @@ int readMtlFile( char *mtllibSource, Material **mat, int *mtl ) { // read a *.mt char lineBuffer[MAX_LENGTH + 1], fileName[MAX_LENGTH + 1] = "", mtlLibTag[MAX_LENGTH + 1]; long mtlLibLineNr = 1; Material *tmpMat = NULL, *localMat = NULL; - + if ( ( fpIn = fopen( mtllibSource, "r" ) ) == NULL) { return -1; } - + tmpMat = realloc( *mat, (*mtl + 1) * sizeof( Material ) ); if (tmpMat == NULL) { @@ -1145,7 +1147,7 @@ int readMtlFile( char *mtllibSource, Material **mat, int *mtl ) { // read a *.mt localMat = tmpMat; (*mtl)--; // because mtl will be incremented before first use - + // Read each line of .mtl file while ( fgets( lineBuffer, MAX_LENGTH, fpIn ) ) { if ( lineBuffer[ 0 ] == '\n' || lineBuffer[ 0 ] == '\r' || ( sscanf( lineBuffer, " %s", mtlLibTag ) ) == 0 ) // blank line or objTag == "" @@ -1157,7 +1159,7 @@ int readMtlFile( char *mtllibSource, Material **mat, int *mtl ) { // read a *.mt if ( !strcmp( mtlLibTag, "newmtl" ) ) // new material { (*mtl)++; - + tmpMat = realloc( localMat, ( *mtl + 1 ) * sizeof( Material ) ); if (tmpMat == NULL) { @@ -1179,7 +1181,7 @@ int readMtlFile( char *mtllibSource, Material **mat, int *mtl ) { // read a *.mt } fileName[cnt1] = '\0'; // close string localMat[ *mtl ].name = strdup( fileName ); - + // init localMat[ *mtl ].KaR = -1.0; localMat[ *mtl ].KaG = -1.0; localMat[ *mtl ].KaB = -1.0; localMat[ *mtl ].KdR = -1.0; localMat[ *mtl ].KdG = -1.0; localMat[ *mtl ].KdB = -1.0; @@ -1246,7 +1248,7 @@ int readMtlFile( char *mtllibSource, Material **mat, int *mtl ) { // read a *.mt { int cnt = 0; int bufLength = strlen( lineBuffer ); - + /* Extract file name from buffer string, then replace file name spaces (if any) by underscores */ while ( ( cnt < bufLength ) && ( isspace( lineBuffer[ cnt ] ) ) ) // look for next non-blank character cnt++; @@ -1259,11 +1261,11 @@ int readMtlFile( char *mtllibSource, Material **mat, int *mtl ) { // read a *.mt fileName[0] = '\0'; sscanf( lineBuffer, " map_Kd_%s", fileName ); - if ( fileName[0] == '\0' ) + if ( fileName[0] == '\0' ) { fprintf( stderr, " Warning in file '%s' line #%ld: map_Kd has no file name.\n", mtllibSource, mtlLibLineNr ); } - else if ( fileName[0] != '-' ) + else if ( fileName[0] != '-' ) { localMat[ *mtl ].texName = strdup( fileName ); } @@ -1315,7 +1317,8 @@ int readMtlFile( char *mtllibSource, Material **mat, int *mtl ) { // read a *.mt *mat = localMat; } fclose( fpIn ); - + fpIn = NULL; + (*mtl)++; // because mtl was decremented before entering main reading loop return EXIT_SUCCESS; diff --git a/tools/v3dconverter/src/utils.c b/tools/v3dconverter/src/utils.c index f40d247..f416b67 100644 --- a/tools/v3dconverter/src/utils.c +++ b/tools/v3dconverter/src/utils.c @@ -53,24 +53,24 @@ long file_size(const char *filename) /* Parse a full file and fill path, baseName, and extention strings. - * + * * Examples: - * + * * fullFileName = "./a/such/full/name.txt" returns: * path = "./a/such/full/" * baseName = "name" * extension = ".txt" - * + * * fullFileName = "a/such/path/" returns: * path = "a/such/path/" * baseName = "" * extension = "" - * + * */ void scanFileName( const char *fullFileName, char **path, char **baseName, char **extension ) { int cnt0 = 0, fullFileNameLength = 0, lastSlashPos = 0, fullPathLength = 0, lastDotPos = 0; - + if ( *path != NULL ) { free( *path ); @@ -86,7 +86,7 @@ void scanFileName( const char *fullFileName, char **path, char **baseName, char free( *extension ); *extension = NULL; } - + /* full file name length */ fullFileNameLength = strlen( fullFileName ); @@ -96,17 +96,17 @@ void scanFileName( const char *fullFileName, char **path, char **baseName, char if ( ( *( fullFileName + lastSlashPos ) == '/' ) || ( *( fullFileName + lastSlashPos ) == '\\' ) ) break; } - + /* path length */ fullPathLength = lastSlashPos + 1; - + /* last dot position */ for ( lastDotPos = fullFileNameLength; lastDotPos > fullPathLength; lastDotPos-- ) { if ( *( fullFileName + lastDotPos ) == '.' ) break; } - + /* extract base name, path, and extension */ if ( lastSlashPos < 0 && lastDotPos == 0) // full file name has NO path and NO extension { @@ -120,7 +120,7 @@ void scanFileName( const char *fullFileName, char **path, char **baseName, char for ( cnt0 = 0; cnt0 < fullFileNameLength; cnt0++ ) *( *baseName + cnt0 ) = *( fullFileName + cnt0 ); *( *baseName + cnt0 ) = '\0'; // close string - + /* no path */ *path = malloc ( sizeof (char) ); if ( *path == NULL ) @@ -129,7 +129,7 @@ void scanFileName( const char *fullFileName, char **path, char **baseName, char exit (EXIT_FAILURE); } **path = '\0'; // close string - + /* no extension */ *extension = malloc ( sizeof (char) ); if ( *extension == NULL ) @@ -138,7 +138,7 @@ void scanFileName( const char *fullFileName, char **path, char **baseName, char exit (EXIT_FAILURE); } **extension = '\0'; // close string - + } else if ( lastDotPos == fullPathLength ) // full file name has NO extension { @@ -149,13 +149,13 @@ void scanFileName( const char *fullFileName, char **path, char **baseName, char fprintf(stderr, "Memory allocation error.\n"); exit (EXIT_FAILURE); } - + for ( cnt0 = fullPathLength; cnt0 < fullFileNameLength; cnt0++ ) { *( *baseName - fullPathLength + cnt0 ) = *( fullFileName + cnt0 ); } *( *baseName - fullPathLength + cnt0 ) = '\0'; // close string - + /* extract path */ *path = malloc ( ( lastSlashPos + 2 ) * sizeof (char) ); if ( *path == NULL ) @@ -168,7 +168,7 @@ void scanFileName( const char *fullFileName, char **path, char **baseName, char *( *path + cnt0 ) = *( fullFileName + cnt0 ); } *( *path + cnt0 ) = '\0'; // close string - + /* no extension */ *extension = malloc ( sizeof (char) ); if ( *extension == NULL ) @@ -190,7 +190,7 @@ void scanFileName( const char *fullFileName, char **path, char **baseName, char for ( cnt0 = fullPathLength; cnt0 < lastDotPos; cnt0++ ) *( *baseName - fullPathLength + cnt0 ) = *( fullFileName + cnt0 ); *( *baseName - fullPathLength + cnt0 ) = '\0'; // close string - + /* extract path */ *path = malloc ( ( lastSlashPos + 2 ) * sizeof (char) ); if ( *path == NULL ) @@ -203,7 +203,7 @@ void scanFileName( const char *fullFileName, char **path, char **baseName, char *( *path + cnt0 ) = *( fullFileName + cnt0 ); } *( *path + cnt0 ) = '\0'; // close string - + /* extract extension */ *extension = malloc ( ( fullFileNameLength - lastDotPos + 1 ) * sizeof (char) ); if ( *extension == NULL ) @@ -224,7 +224,7 @@ int calculateSurfaceNormal ( Vcoord *v, int verticies, Vnormal *vn ) { // Newell's method, from: https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal pseudo-code // Returns EXIT_FAILURE (0) in case of div by zero error - + Vcoord vCurrent, vNext; int cnt1 = 0; @@ -232,27 +232,27 @@ int calculateSurfaceNormal ( Vcoord *v, int verticies, Vnormal *vn ) vn->i = 0; vn->j = 0; vn->k = 0; - + for ( int cnt0 = 0; cnt0 < verticies; cnt0++ ) { vCurrent.x = v[ cnt0 ].x; vCurrent.y = v[ cnt0 ].y; vCurrent.z = v[ cnt0 ].z; - + if ( ( cnt0 + 1 ) >= verticies ) cnt1 = 0; else cnt1 = cnt0 + 1; - + vNext.x = v[ cnt1 ].x; vNext.y = v[ cnt1 ].y; vNext.z = v[ cnt1 ].z; - + vn->i += ( vCurrent.y + vNext.y ) * ( vCurrent.z - vNext.z ); vn->j += ( vCurrent.z + vNext.z ) * ( vCurrent.x - vNext.x ); vn->k += ( vCurrent.x + vNext.x ) * ( vCurrent.y - vNext.y ); } - + // normalize double vecLength = sqrt( vn->i * vn->i + vn->j * vn->j + vn->k * vn->k ); if ( vecLength == 0 ) @@ -277,15 +277,15 @@ char *timeStamp() // returns a YYYYMMDDHHMMSS timestamp time(&now); // Convert to local time struct tm *local = localtime(&now); - h = local->tm_hour; - min = local->tm_min; - s = local->tm_sec; - day = local->tm_mday; - month = local->tm_mon + 1; - year = local->tm_year + 1900; + h = local->tm_hour; + min = local->tm_min; + s = local->tm_sec; + day = local->tm_mday; + month = local->tm_mon + 1; + year = local->tm_year + 1900; timeStampString = strdup("YYYYMMDDHHMMSS"); - sprintf(timeStampString, "%02d%02d%d%02d%02d%02d", year, month, day, h, min, s); + sprintf(timeStampString, "%04d%02d%02d%02d%02d%02d", year, month, day, h, min, s); return(timeStampString); } @@ -294,9 +294,9 @@ char *timeStamp() // returns a YYYYMMDDHHMMSS timestamp bool isTex ( const char *fullFileName ) // returns 'true' if file baseName contains '.tex' { char *path = NULL, *baseName = NULL, *extension = NULL; - + scanFileName( fullFileName, &path, &baseName, &extension ); - + if ( strstr( baseName, ".tex" ) ) return true; else @@ -307,9 +307,9 @@ bool isTex ( const char *fullFileName ) // returns 'true' if file baseName conta bool isHf ( const char *fullFileName ) // returns 'true' if file baseName contains '.hf' { char *path = NULL, *baseName = NULL, *extension = NULL; - + scanFileName( fullFileName, &path, &baseName, &extension ); - + if ( strstr( baseName, ".hf" ) ) return true; else diff --git a/tools/v3dconverter/src/v3dconverter.c b/tools/v3dconverter/src/v3dconverter.c index 08603b3..a2ab224 100644 --- a/tools/v3dconverter/src/v3dconverter.c +++ b/tools/v3dconverter/src/v3dconverter.c @@ -43,7 +43,7 @@ int main( int argc, char *argv[] ) char *scaleString; char lineBuffer[MAX_LENGTH + 1], objTag[MAX_LENGTH + 1]; FILE *fp; - + struct UserModifier userModifier; userModifier.rx = 0; userModifier.ry = 0; @@ -53,7 +53,8 @@ int main( int argc, char *argv[] ) userModifier.tz = 0; userModifier.scale = 1.0; userModifier.invertFaces = false; - + userModifier.doNotTransformModels = false; + if ( argc == 1 ) { fprintf( stdout, "Try \"%s -h\" for help.\n", argv[0] ); @@ -67,6 +68,8 @@ int main( int argc, char *argv[] ) fprintf( stdout, " OPTIONS:\n"); fprintf( stdout, " -if\n"); fprintf( stdout, " Invert faces visibility (flip winding).\n"); + fprintf( stdout, " -do-not-transform\n"); + fprintf( stdout, " Do not apply any translation and/or rotation when converting a *.3d model or *.hf terrain to an other file format: usefull to center a terrain tile in Blender or to understand how to create rotors, gears, ailerons, and other moving parts.\n"); fprintf( stdout, " -ro x y z\n"); fprintf( stdout, " Rotate object of x, y, and z degrees around respectives axes.\n" ); fprintf( stdout, " -sc scale\n"); @@ -95,10 +98,10 @@ int main( int argc, char *argv[] ) fprintf( stdout, " . : Not planned.\n" ); fprintf( stdout, " For other formats, try assimp: https://www.assimp.org/\n" ); fprintf( stdout, " \n" ); - + return 0; } - + /* Read command line arguments */ for ( unsigned short counter = 1; counter < argc; counter++ ) { @@ -126,6 +129,10 @@ int main( int argc, char *argv[] ) { userModifier.invertFaces = true; } + else if ( !strcmp(argv[counter], "-do-not-transform") ) // do not apply rotate / translate during *.3d to *.obj conversion + { + userModifier.doNotTransformModels = true; + } else if ( !strcmp(argv[counter], "-sc") ) // scale model { if ( counter < argc ) @@ -135,7 +142,7 @@ int main( int argc, char *argv[] ) fprintf( stderr, "Error while reading scale.\n" ); return 0; } - + if ( memchr( scaleString, '/', strlen(scaleString) ) != NULL ) { long dividend = 1, divider = 1; @@ -144,7 +151,7 @@ int main( int argc, char *argv[] ) } else sscanf( scaleString, "%lf", &userModifier.scale ); - + if ( userModifier.scale < 0 ) { fprintf( stdout, "\033[7mNegative scale will produce strange results. You've been warned!\033[0m\n" ); @@ -206,7 +213,7 @@ int main( int argc, char *argv[] ) return 0; } } - + /* Convert */ if ( !strcmp( extOf( source ), ".3d") && !strcmp( extOf( destination ), ".obj" ) ) { @@ -239,7 +246,7 @@ int main( int argc, char *argv[] ) ; } fclose( fp ); - + /* Select the right function, depending of *.3d file type ("object" or "terrain") */ if ( !hasHeightfield ) { // it is *.3d object file @@ -302,6 +309,11 @@ int main( int argc, char *argv[] ) userModifier.ty = 0; userModifier.tz = 0; } + if ( userModifier.invertFaces == true ) + { + fprintf( stdout, "[ INFO ] -do-not-transform (do not apply any translation and/or rotation) option currently not available in *.ac to *.3d conversion.\n" ); + userModifier.doNotTransformModels = false; + } ac3dToV3d( source, destination, &userModifier ); } else diff --git a/tools/v3dconverter/src/v3dconverter.h b/tools/v3dconverter/src/v3dconverter.h index 50579c3..7b4de11 100644 --- a/tools/v3dconverter/src/v3dconverter.h +++ b/tools/v3dconverter/src/v3dconverter.h @@ -15,9 +15,10 @@ ***********************************************************************/ -#define VERSION "0.3.2" +#define VERSION "0.3.3" #include // for PATH_MAX +#include #define MAXPATHLENGTH PATH_MAX #define MAX_LENGTH 2048 @@ -26,6 +27,12 @@ #define OBJ_OBJECT_NAME_TERRAIN "Terrain" #define OBJ_OBJECT_NAME_V3DCONVERTER_DATA "v3dConverter_data_DO_NOT_REMOVE" +// TGA image descriptor, bits 5 & 4 +#define TGA_BOTTOM_LEFT_MASK 0b00000000 +#define TGA_BOTTOM_RIGHT_MASK 0b00010000 +#define TGA_TOP_LEFT_MASK 0b00100000 +#define TGA_TOP_RIGHT_MASK 0b00110000 + typedef struct UserModifier UserModifier; struct UserModifier { double rx; @@ -35,7 +42,8 @@ struct UserModifier { double ty; double tz; double scale; - unsigned short invertFaces; + bool invertFaces; + bool doNotTransformModels; }; // vertex geometric coords diff --git a/tools/v3dconverter/src/v3dhftoobj.c b/tools/v3dconverter/src/v3dhftoobj.c index a80c4dd..b2c5c49 100644 --- a/tools/v3dconverter/src/v3dhftoobj.c +++ b/tools/v3dconverter/src/v3dhftoobj.c @@ -39,7 +39,7 @@ int v3dHfToObj( const char *source, const char *dest, UserModifier *userModifier long lineNr = 1; unsigned int triangles = 0; bool hasHeightfield = false, hasTexture = false; - + struct Terrain { char path[ MAX_LENGTH + 1 ]; // texture path double sizeX; // "width", in meters @@ -52,23 +52,23 @@ int v3dHfToObj( const char *source, const char *dest, UserModifier *userModifier double p; // pitch rotation, in degrees double b; // bank rotation, in degrees }; - + struct TexLoad { char name[MAX_LENGTH + 1]; // texture name char path[MAX_LENGTH + 1]; // texture path double priority; }; - + struct TgaMinimalHeader { uint8_t idFieldLength; // Byte 0: image ID field length uint8_t colorMapType; uint8_t imageType; - + /* Color Map Specification */ uint16_t colorMapIndex; // Byte 3 (LSB), byte 4 (MSB) uint16_t colorMapLength; uint8_t colorMapSize; - + /* Image specification */ uint16_t xOrigin; // Byte 8 (LSB), byte 9 (MSB) uint16_t yOrigin; @@ -77,20 +77,20 @@ int v3dHfToObj( const char *source, const char *dest, UserModifier *userModifier uint8_t pixelDepth; // Byte 16 uint8_t imageDescriptor; }; - + struct Terrain terrain; - + struct TexLoad texLoad; strcpy( texLoad.name, "" ); strcpy( texLoad.path, "" ); - + /* * Generate a time stamp string. This string will be used to name material and to name * the file which will be used to store terrain data for later *.obj to *.hf conversion. */ char *timeStampString = timeStamp(); - - + + /* * Create and open "timeStampString" file to store terrain data * for later *.obj to *.hf conversion. @@ -99,40 +99,23 @@ int v3dHfToObj( const char *source, const char *dest, UserModifier *userModifier char v3dConverterPath[] = "/.local/share/v3dconverter/"; int dirLength = strlen( v3dConverterPath ); int fileNameLength = strlen( timeStampString ); - - char *name = malloc( homeLength + dirLength + fileNameLength + 1 ); + + char *name = calloc( homeLength + dirLength + fileNameLength + 1, sizeof(char) ); if ( name == NULL ) { fprintf( stderr, "v3dhftoobj.c: can't allocate memory for name.\n" ); return -1; } - + strcat( name, getenv("HOME") ); strcat( name, v3dConverterPath ); - + errno = 0; - if ( ( mkdir(name, S_IRWXU) ) == -1 ) { - switch ( errno ) { - case EEXIST: - //fprintf( stderr, "v3dhftoobj.c: pathname already exists.\n"); - break; - case EACCES : - fprintf( stderr, "v3dhftoobj.c: the parent directory does not allow write.\n"); - //exit(EXIT_FAILURE); - break; - case ENAMETOOLONG: - fprintf( stderr, "v3dhftoobj.c: pathname is too long.\n"); - //exit(EXIT_FAILURE); - break; - default: - fprintf( stderr, "v3dhftoobj.c: mkdir error.\n"); - //exit(EXIT_FAILURE); - break; - } - } - + if ( mkdir(name, S_IRWXU) == -1 && errno != EEXIST ) + fprintf( stderr, "v3dhftoobj.c: %s : %s\n", name, strerror(errno)); + strcat( name, timeStampString ); - + if ( ( fpTerrainDataOut = fopen( name, "w" ) ) == NULL ) { perror( name ); return -1; @@ -141,8 +124,8 @@ int v3dHfToObj( const char *source, const char *dest, UserModifier *userModifier { free( name ); } - - + + /* * Open *.3d file, then look for heightfield and texture names. */ @@ -161,49 +144,68 @@ int v3dHfToObj( const char *source, const char *dest, UserModifier *userModifier { hasHeightfield = true; int sscanfResult = 0; - sscanfResult = sscanf( lineBuffer, "%*s %s %lf %lf %lf %lf %lf %lf %lf %lf %lf", + char additionalParameter[MAX_LENGTH]; + sscanfResult = sscanf( lineBuffer, "%*s %s %lf %lf %lf %lf %lf %lf %lf %lf %lf %s", terrain.path, &terrain.sizeX, &terrain.sizeY, &terrain.maxAltitude, &terrain.xPos, &terrain.yPos, &terrain.zPos, - &terrain.h, &terrain.p, &terrain.b + &terrain.h, &terrain.p, &terrain.b, + additionalParameter ); if ( sscanfResult != 10 ) { int cnt = 0; while ( lineBuffer[ cnt++ ] != '\n' ); lineBuffer[ --cnt ] = '\0'; - fprintf( stderr, "v3dhftoobj.c: Error while scanning line #%ld (\"%s\").\n", lineNr, lineBuffer ); + if ( sscanfResult < 10 ) + fprintf( stderr, "v3dhftoobj.c: Error while scanning line #%ld \"%s\": %d parameter(s) missing.\n", lineNr, lineBuffer, 10 - sscanfResult ); + else + fprintf( stderr, "v3dhftoobj.c: Warning while scanning line #%ld \"%s\": too much parameters.\n", lineNr, lineBuffer); + } + + /* Wants user to not apply *.3d terrain translations? */ + if ( userModifier->doNotTransformModels == true ) + { + terrain.xPos = 0; + terrain.yPos = 0; + terrain.zPos = 0; + //printf( "Info: -do-not-transform option activated.\n); } } else if ( !strcmp( objTag, "texture_load" ) ) { hasTexture = true; int sscanfResult = 0; - sscanfResult = sscanf( lineBuffer, "%*s %s %s %lf", + char additionalParameter[MAX_LENGTH]; + sscanfResult = sscanf( lineBuffer, "%*s %s %s %lf %s", texLoad.name, texLoad.path, - &texLoad.priority + &texLoad.priority, + additionalParameter ); if ( sscanfResult != 3 ) { int cnt = 0; while ( lineBuffer[ cnt++ ] != '\n' ); lineBuffer[ --cnt ] = '\0'; - fprintf( stderr, "v3dhftoobj.c: Error while scanning line #%ld (\"%s\").\n", lineNr, lineBuffer ); + if ( sscanfResult < 3 ) + fprintf( stderr, "v3dhftoobj.c: Error while scanning line #%ld \"%s\": %d parameter(s) missing.\n", lineNr, lineBuffer, 3 - sscanfResult ); + else + fprintf( stderr, "v3dhftoobj.c: Warning while scanning line #%ld \"%s\": too much parameters.\n", lineNr, lineBuffer); } } else ; - + if ( hasHeightfield && hasTexture ) break; - + lineNr++; } fclose( fp3dFileIn ); fp3dFileIn = NULL; - - + + /* * Read *.hf image header. */ @@ -214,17 +216,34 @@ int v3dHfToObj( const char *source, const char *dest, UserModifier *userModifier if ( header == NULL ) { fprintf( stderr, "Can't allocate memory for TGA image header.\n" ); - + FREE_AND_CLOSE_ALL_V3DHFTOOBJ return -1; } + + /* Try to extract texture full path */ + char * str_tmp_0 = strdup( source ); + char * found = strstr( str_tmp_0, "/data/objects/" ); + /* String found? */ + if ( found != NULL ) { + char * str_tmp_1 = strdup( source ); + + str_tmp_0[strlen(source) - strlen(found) + 1] = '\0'; + sprintf( str_tmp_1, "%sdata/%s", str_tmp_0, terrain.path ); + + strcpy( terrain.path, str_tmp_1 ); + + free(str_tmp_1); + } + free(str_tmp_0); + if ( (fpHfIn = fopen( terrain.path, "rb") ) == NULL ) { perror( terrain.path ); - + FREE_AND_CLOSE_ALL_V3DHFTOOBJ return -1; } - + /* Warning: sizeof( hfHeader ) don't work for image reading because of memory alignment. */ int tgaImageHeaderLength = 18; fread( header, 1, tgaImageHeaderLength, fpHfIn ); @@ -240,7 +259,7 @@ int v3dHfToObj( const char *source, const char *dest, UserModifier *userModifier hfHeader.height = header[14] + header[15] * 256; hfHeader.pixelDepth = header[16]; hfHeader.imageDescriptor= header[17]; - /* +/* long fileSize = 0; fileSize = file_size( terrain.path ); fprintf( stderr, "------ *.hf image: %s, %ld bytes ------\n", terrain.path, fileSize ); @@ -255,8 +274,8 @@ int v3dHfToObj( const char *source, const char *dest, UserModifier *userModifier fprintf( stderr, " width = %d ( %d + %d * 256 )\n", hfHeader.width, header[12], header[13] ); fprintf( stderr, " height = %d ( %d + %d * 256 )\n", hfHeader.height, header[14], header[15] ); fprintf( stderr, " pixelDepth = %d\n", hfHeader.pixelDepth ); - fprintf( stderr, "imageDescriptor = %d\n\n", hfHeader.imageDescriptor ); - */ + fprintf( stderr, "imageDescriptor = %08b\n\n", hfHeader.imageDescriptor ); +*/ if ( hfHeader.imageType != 3 ) { fprintf( stderr, "Warning: *.hf TGA image should preferably be of type 3 (ie an uncompressed grayscale image).\n" ); @@ -265,31 +284,30 @@ int v3dHfToObj( const char *source, const char *dest, UserModifier *userModifier { fprintf( stderr, "Warning: *.hf TGA image should preferably have 8 bits per pixel (%d bits found).\n", hfHeader.pixelDepth ); } - + imageOrigin = hfHeader.imageDescriptor & 0b00110000; - imageOrigin = imageOrigin >> 4; - if ( imageOrigin == 2 ) + if ( imageOrigin == TGA_TOP_LEFT_MASK ) ; // top left origin, okay. - else if ( imageOrigin == 0 ) + else if ( imageOrigin == TGA_BOTTOM_LEFT_MASK ) { fprintf( stderr, "Warning: *.hf TGA image should preferably be top left origin (bottom left found and accepted).\n" ); } - else if ( imageOrigin == 1 ) + else if ( imageOrigin == TGA_BOTTOM_RIGHT_MASK ) { fprintf( stderr, "Error: *.hf TGA image should preferably be top left origin (bottom right found).\n" ); - + FREE_AND_CLOSE_ALL_V3DHFTOOBJ return -1; } - else // imageOrigin == 3 + else // imageOrigin == TGA_TOP_RIGHT_MASK { fprintf( stderr, "Error: *.hf TGA image should preferably be top left origin (top right found).\n" ); - + FREE_AND_CLOSE_ALL_V3DHFTOOBJ return -1; } - - + + /* * Read ID field (if any). We don't need it, but this will increment file data pointer. */ @@ -299,8 +317,8 @@ int v3dHfToObj( const char *source, const char *dest, UserModifier *userModifier fread( imageID, 1, hfHeader.idFieldLength, fpHfIn ); free( imageID ); } - - + + /* * Read color map field (if any). We don't need it, but this will increment file data pointer. */ @@ -311,14 +329,14 @@ int v3dHfToObj( const char *source, const char *dest, UserModifier *userModifier fread( colorMap, 1, colorMapBytes, fpHfIn ); free( colorMap ); fprintf( stderr, "Error: *.hf TGA image shouldn't have any color map (%d length color map found).\n", hfHeader.colorMapLength); - + FREE_AND_CLOSE_ALL_V3DHFTOOBJ return -1; } free( header ); header = NULL; - - + + /* * Generate output (*.obj and *.mtl) file names. */ @@ -327,37 +345,37 @@ int v3dHfToObj( const char *source, const char *dest, UserModifier *userModifier sprintf( buffer, "%s.obj", baseName ); else sprintf( buffer, "%s/%s.obj", path, baseName ); - + if ( ( fpObjOut = fopen(buffer, "w" ) ) == NULL) { perror(buffer); - + FREE_AND_CLOSE_ALL_V3DHFTOOBJ return -1; } - + if ( path[0] == '\0' ) // if no path sprintf( buffer, "%s.mtl", baseName ); else sprintf( buffer, "%s/%s.mtl", path, baseName ); - + if ( ( fpMtlOut = fopen( buffer, "w") ) == NULL ) { perror( buffer ); - + FREE_AND_CLOSE_ALL_V3DHFTOOBJ return -1; } - - + + /* * Write *.mtl file name in *.obj file and write material in *.mtl file. */ if ( fpMtlOut != NULL ) { /* Write terrain object name in *obj file */ //fprintf( fpObjOut, "o Terrain\n\n" ); - + /* Write material file name in *obj file, and add texture file name (if any) */ fprintf( fpObjOut, "mtllib ./%s.mtl\n\n", baseName ); - + /* Create material in *.mtl file */ fprintf( fpMtlOut, "newmtl %s\n", timeStampString ); fprintf( fpMtlOut, "Ns 127.999985\n" ); @@ -368,14 +386,14 @@ int v3dHfToObj( const char *source, const char *dest, UserModifier *userModifier fprintf( fpMtlOut, "Ni 1.450000\n" ); fprintf( fpMtlOut, "d 1.000000\n" ); fprintf( fpMtlOut, "illum 2\n" ); - if ( texLoad.path != NULL ) + if ( strlen(texLoad.path) != 0 ) fprintf( fpMtlOut, "map_Kd %s\n", texLoad.path ); - + fclose( fpMtlOut ); fpMtlOut = NULL; } - - + + /* * Read *.hf altitudes. */ @@ -384,12 +402,12 @@ int v3dHfToObj( const char *source, const char *dest, UserModifier *userModifier if ( altitudeData == NULL ) { fprintf( stderr, "Can't allocate memory for altitudeData.\n" ); - + FREE_AND_CLOSE_ALL_V3DHFTOOBJ return -1; } - - if ( imageOrigin == 0 ) + + if ( imageOrigin == TGA_BOTTOM_LEFT_MASK ) { /* Image is bottom left origin: rows order must be inverted in order to obtain a top left origin image. */ unsigned long bytesPerRow = hfHeader.width * bytesPerPixel; @@ -397,7 +415,7 @@ int v3dHfToObj( const char *source, const char *dest, UserModifier *userModifier if ( tempRow == NULL ) { fprintf( stderr, "Can't allocate memory for tempRow.\n" ); - + FREE_AND_CLOSE_ALL_V3DHFTOOBJ return -1; } @@ -408,7 +426,7 @@ int v3dHfToObj( const char *source, const char *dest, UserModifier *userModifier } free( tempRow ); } - else if ( imageOrigin == 2) + else if ( imageOrigin == TGA_TOP_LEFT_MASK) { /* Image is top left origin: we just have to read data. */ fread( altitudeData, 1, hfHeader.width * hfHeader.height * bytesPerPixel, fpHfIn ); @@ -417,17 +435,17 @@ int v3dHfToObj( const char *source, const char *dest, UserModifier *userModifier { ; /* Other cases (imageOrigin == 1 or imageOrigin == 2) have been detected and have generated an error earlier */ } - - + + /* * Write terrain vertices. */ long offset = 0; - double perPixelXDistance = terrain.sizeX / (double)hfHeader.width; - double perPixelYDistance = terrain.sizeY / (double)hfHeader.height; + double perPixelXDistance = terrain.sizeX / (double)( hfHeader.width - 1 ); + double perPixelYDistance = terrain.sizeY / (double)( hfHeader.height - 1 ); double perPixelZDistance = terrain.maxAltitude / 255; // altitude is always an 8 bits value - double halfXSize = terrain.sizeX / 2 - perPixelXDistance / 2; - double halfYSize = terrain.sizeY / 2 - perPixelYDistance / 2; + double halfXSize = terrain.sizeX / 2; + double halfYSize = terrain.sizeY / 2; for ( int line = hfHeader.height - 1; line >= 0; line-- ) { for ( int col = 0; col < hfHeader.width; col++ ) @@ -436,60 +454,60 @@ int v3dHfToObj( const char *source, const char *dest, UserModifier *userModifier double y = (double)line * perPixelYDistance - halfYSize; double z = (double)altitudeData[ offset ] * perPixelZDistance; offset += bytesPerPixel; - + /* Apply *.3d terrain translations */ x += terrain.xPos; y += terrain.yPos; z += terrain.zPos; - + /* Apply user translations */ x += userModifier->tx; y += userModifier->ty; z += userModifier->tz; - + /* Apply user scale */ x *= userModifier->scale; y *= userModifier->scale; z *= userModifier->scale; - + /* Write vertex to *.obj file */ - fprintf( fpObjOut, "v %.3f %.3f %.3f\n", x, z, -y ); + fprintf( fpObjOut, "v %.6f %.6f %.6f\n", x, z, -y ); } } free( altitudeData ); - - + + /* * Write texture vertices, if any. */ bool isTextured = true; - if ( texLoad.path == NULL ) + if ( strlen(texLoad.path) == 0 ) { isTextured = false; } else /* There is a texture to apply */ { /* Write texture vertices in *.obj file */ - for ( int cnt0 = hfHeader.height; cnt0 >= 0; cnt0-- ) + for ( int line = hfHeader.height - 1; line >= 0; line-- ) { - for ( int cnt1 = 0; cnt1 < hfHeader.width; cnt1++ ) + for ( int col = 0; col < hfHeader.width; col++ ) { - fprintf( fpObjOut, "vt %.6f %.6f\n", (double)cnt1 / (double)hfHeader.width, (double)cnt0 / (double)hfHeader.height ); + fprintf( fpObjOut, "vt %.6f %.6f\n", (double)col / (double)(hfHeader.width - 1), (double)line / (double)(hfHeader.height - 1) ); } } } - + /* * Write material name. */ fprintf( fpObjOut, "usemtl %s\n", timeStampString ); - + /* * Set smooth rendering. */ fprintf( fpObjOut, "s 1\n" ); - - + + /* * Write terrain faces. */ @@ -509,7 +527,7 @@ int v3dHfToObj( const char *source, const char *dest, UserModifier *userModifier hOffset + 1, hOffset + 1, hOffset, hOffset ); - + /* second triangle */ fprintf(fpObjOut, "f %d/%d %d/%d %d/%d\n", hOffset, hOffset, hOffset + hfHeader.width, hOffset + hfHeader.width, @@ -520,15 +538,15 @@ int v3dHfToObj( const char *source, const char *dest, UserModifier *userModifier { /* first triangle */ fprintf(fpObjOut, "f %d %d %d\n", hOffset + 1 + hfHeader.width, hOffset + 1, hOffset ); - + /* second triangle */ fprintf(fpObjOut, "f %d %d %d\n", hOffset, hOffset + hfHeader.width, hOffset + 1 + hfHeader.width ); } triangles += 2; } } - - + + /* * Store *.3d terrain data for later *.obj to *.hf conversion. */ @@ -546,8 +564,8 @@ int v3dHfToObj( const char *source, const char *dest, UserModifier *userModifier fprintf( fpTerrainDataOut,"terrain_bitsHeight=%d\n", hfHeader.height ); fclose( fpTerrainDataOut ); fpTerrainDataOut = NULL; - - + + /* * Print some info to console. */ @@ -557,8 +575,8 @@ int v3dHfToObj( const char *source, const char *dest, UserModifier *userModifier printf( "./ directory.\n" ); else printf( "%s directory.\n", path ); - - + + FREE_AND_CLOSE_ALL_V3DHFTOOBJ return 0; } diff --git a/tools/v3dconverter/src/v3dtoobj.c b/tools/v3dconverter/src/v3dtoobj.c index add3725..2994cc0 100644 --- a/tools/v3dconverter/src/v3dtoobj.c +++ b/tools/v3dconverter/src/v3dtoobj.c @@ -57,7 +57,7 @@ long readV3dVerticies ( FILE *fpIn, const char * endBoundString, FILE *fpOut, ch lineNr++; // input file line counter break; } - + if ( !strcmp(strtoupper( result, buffer), "") ) { // it's an empty line break; } @@ -78,13 +78,13 @@ long readV3dVerticies ( FILE *fpIn, const char * endBoundString, FILE *fpOut, ch isAVertexNormal = 1; // new "NORMAL" statement found, first "NORMAL" statement was a vertex normal } fsetpos(fpIn, &position); // return to original in-file position - + if ( isAVertexNormal == 1 ) { vn++; sscanf( lineBuffer, "%*s %lf %lf %lf", &nx, &ny, &nz ); fprintf( fpOut, "vn "); - + // apply v3d rotation and position modifiers if ( modifier.rh ) RotateZ ( &nx, &ny, &nz, &pw, modifier.rh ); if ( modifier.rp ) RotateX ( &nx, &ny, &nz, &pw, modifier.rp ); @@ -92,7 +92,7 @@ long readV3dVerticies ( FILE *fpIn, const char * endBoundString, FILE *fpOut, ch nx += modifier.tx; ny += modifier.ty; nz += modifier.tz; - + /* TODO apply user modifiers. Apply order is: X rotation, then Y rotation, then Z rotation, then scale, then translations. */ /* if ( userModifier->rx != 0.0 ) @@ -108,11 +108,11 @@ long readV3dVerticies ( FILE *fpIn, const char * endBoundString, FILE *fpOut, ch RotateZ ( &nx, &ny, &nz, &pw, userModifier->rz ); } */ - + /* re-orient normal them because v3d coords system is different from obj coords system */ RotateZ ( &nx, &ny, &nz, &pw, -90 ); // rotate normal arround Z axis. Angle given in degrees. RotateX ( &nx, &ny, &nz, &pw, 90 ); // rotate normal arround X axis. Angle given in degrees. - + // write vertex normal fprintf( fpOut, "%lf %lf %lf\n", nx, ny, nz ); } @@ -121,7 +121,7 @@ long readV3dVerticies ( FILE *fpIn, const char * endBoundString, FILE *fpOut, ch vt++; fprintf( fpOut, "vt "); sscanf( lineBuffer, "%*s %lf %lf", &tu, &tv ); // u, v - + // write texture coords fprintf( fpOut, "%lf %lf\n", tu, tv ); // u, v } @@ -131,11 +131,11 @@ printf("TEXTURE VERTEX FOUND at line #%ld\n", lineNr); vt++; fprintf( fpOut, "vt "); sscanf( lineBuffer, "%*s %lf %lf %lf", &x, &y, &z ); // u, v, w - + //if ( modifier.rh ) RotateZ ( &x, &y, &z, &pw, modifier.rh ); //if ( modifier.rp ) RotateX ( &x, &y, &z, &pw, modifier.rp ); //if ( modifier.rb ) RotateY ( &x, &y, &z, &pw, modifier.rb ); - + //x += modifier.tx; //y += modifier.ty; //z += modifier.tz; @@ -146,7 +146,7 @@ printf("TEXTURE VERTEX FOUND at line #%ld\n", lineNr); v++; fprintf( fpOut, "v "); sscanf( lineBuffer, "%lf %lf %lf", &vx, &vy, &vz ); - + if ( textureOrient[0] != '\0' ) { // if there is a running "texture_orient_" command, then create a texture vertex vt++; sscanf( textureOrient, "%*s %lf %lf %lf %lf", &ta, &tb, &tda, &tdb ); // "texture_orient_" command parameters @@ -170,9 +170,9 @@ printf("TEXTURE VERTEX FOUND at line #%ld\n", lineNr); if ( vz > tb ) tv = ( vz - tb ) / tdb; else tv = ( tb - vz ) / tdb; } - + } - + // apply v3d rotation and position modifiers if ( modifier.rh ) RotateZ ( &vx, &vy, &vz, &pw, modifier.rh ); if ( modifier.rp ) RotateX ( &vx, &vy, &vz, &pw, modifier.rp ); @@ -180,7 +180,7 @@ printf("TEXTURE VERTEX FOUND at line #%ld\n", lineNr); vx += modifier.tx; vy += modifier.ty; vz += modifier.tz; - + /* TODO apply user modifiers. Apply order is: X rotation, then Y rotation, then Z rotation, then scale, then translations. */ /* if ( userModifier->rx != 0.0 ) @@ -196,11 +196,11 @@ printf("TEXTURE VERTEX FOUND at line #%ld\n", lineNr); RotateZ ( &vx, &vy, &vz, &pw, userModifier->rz ); } */ - + /* re-orient vertex because v3d coords system is different from obj coords system */ RotateZ ( &vx, &vy, &vz, &pw, -90 ); // rotate vertex arround Z axis. Angle given in degrees. RotateX ( &vx, &vy, &vz, &pw, 90 ); // rotate vertex arround X axis. Angle given in degrees. - + fprintf( fpOut, "%lf %lf %lf\n", vx, vy, vz ); // write geometric vertex if ( textureOrient[0] != '\0' ) { // write texture vertex fprintf( fpOut, "vt %lf %lf\n", tu, tv ); @@ -341,7 +341,7 @@ printf("TEXTURE VERTEX FOUND at line #%ld\n", lineNr); fprintf(fpOut, " %d/%d/%d", v3, v3, v3 ); } fprintf(fpOut, "\n"); - + // next triangles for ( int cnt0 = primitives - 1; cnt0 > 0; cnt0 = cnt0 - 2 ) { v1 = v1 + 1; @@ -368,7 +368,7 @@ printf("TEXTURE VERTEX FOUND at line #%ld\n", lineNr); fprintf(fpOut, " %d/%d/%d", v3, v3, v3 ); } fprintf(fpOut, "\n"); - + v1 = v1 + 1; if ( v1 < 0 ) { v2 = v2 + 2; @@ -485,7 +485,7 @@ printf("TEXTURE VERTEX FOUND at line #%ld\n", lineNr); fprintf(fpOut, " %d/%d/%d", -primitives * 2 + 1, -primitives * 2 + 1, -primitives * 2 + 1 ); } fprintf(fpOut, "\n"); - + // next quads for ( int cnt0 = primitives - 1; cnt0 > 0; cnt0-- ) { int offset = -cnt0 * 2; @@ -539,10 +539,10 @@ printf("TEXTURE VERTEX FOUND at line #%ld\n", lineNr); primitives = 1; // for printf output } ////////////////////////////////////////////////////printf("%d %s with %d v, %d vt, %d vn\n", primitives, endBoundString, v, vt, vn); - - - - + + + + /* fprintf( fpOut, "****************\n"); fseek(fpIn, startPosition, SEEK_SET); @@ -552,7 +552,7 @@ printf("TEXTURE VERTEX FOUND at line #%ld\n", lineNr); } fprintf( fpOut, "****************\n"); */ - + return primitives; } @@ -564,43 +564,43 @@ int v3dToObj( const char *source, const char *dest, UserModifier *userModifier ) char modelType[22 + 1]; // longest model type: 'aileron_elevator_right' long fileSize = 0; unsigned int points = 0, lines = 0, line_strip = 0, line_loop = 0, triangle = 0, triangle_strip = 0, triangle_fan = 0, quads = 0, quad_strip = 0, polygon = 0, color = 0, texture = 0, objectNr = 0; - + modifier.tx = 0; modifier.ty = 0; modifier.tz = 0; modifier.rh = 0; modifier.rp = 0; modifier.rb = 0; - + textureName[0] = '\0'; // no texture at startup textureOrient[0] = '\0'; // no texture at startup - + fileSize = file_size(source); printf("*.3d file size: %ld bytes.\n", fileSize); - + if ((fpIn = fopen(source, "r")) == NULL) { perror(source); return -1; } - + // Generate output file name scanFileName( dest, &path, &baseName, &extension ); - + if ( path[0] == '\0' ) // if no path sprintf( buffer, "%s.obj", baseName ); else sprintf( buffer, "%s/%s.obj", path, baseName ); - + if ((fpOut = fopen(buffer, "w")) == NULL) { perror(buffer); return -1; } - + if ( path[0] == '\0' ) // if no path sprintf( buffer, "%s.mtl", baseName ); else sprintf( buffer, "%s/%s.mtl", path, baseName ); - + if ((fpMtl = fopen(buffer, "w")) == NULL) { perror(buffer); return -1; @@ -610,7 +610,7 @@ int v3dToObj( const char *source, const char *dest, UserModifier *userModifier ) } /////////////////////////////fprintf(fpOut, "g %s\n", baseName); // create a group and give it 3d model name FIXME: can be an issue if model name contains one ore more spaces! - + /* // count file lines, then create as many verticies as lines number (not efficient but easier way to do the job...) long counter = 0; @@ -624,7 +624,7 @@ int v3dToObj( const char *source, const char *dest, UserModifier *userModifier ) struct Vertex vertex[counter]; rewind(fpIn); */ - + while ( fgets(lineBuffer, MAX_LENGTH, fpIn) ) { strcpy( buffer, ""); // clear buffer sscanf( lineBuffer, "%s %s", buffer, parameter1 ); @@ -687,10 +687,10 @@ int v3dToObj( const char *source, const char *dest, UserModifier *userModifier ) // create material in *.mtl file //char red[20], green[20], blue[20], transparency[20], ambient[20], diffuse[20], specular[20], shininess[20], emission[20]; double red = 0, green = 0, blue = 0, transparency = 0, ambient = 1, diffuse = 1, specular = 1, shininess = 1, emission = 1; - + // TODO FIXME check if material already exists before creating a new one !!! If not, a VERY big quantity of materials can be generated and Blender don't like it!!! fprintf(fpMtl, "newmtl color_%d\n", color); - + if ( sscanf( lineBuffer, " %*s %lf %lf %lf %lf %lf %lf %lf %lf %lf", &red, &green, &blue, &transparency, &ambient, &diffuse, &specular, &shininess, &emission ) ) { /* fprintf(fpMtl, "Ka %lf %lf %lf\n", red * ambient, green * ambient, blue * ambient); @@ -699,11 +699,11 @@ int v3dToObj( const char *source, const char *dest, UserModifier *userModifier ) fprintf(fpMtl, "Ka 0.200000 0.200000 0.200000\n"); fprintf(fpMtl, "Ks 1.000000 1.000000 1.000000\n"); fprintf(fpMtl, "Kd %lf %lf %lf\n", red * diffuse, green * diffuse, blue * diffuse); - + // Ka r g b : The Ka statement specifies the ambient reflectivity using RGB values. // Kd r g b : The Kd statement specifies the diffuse reflectivity using RGB values. // Ks r g b : The Ks statement specifies the specular reflectivity using RGB values. - // Illumination Properties that are turned on in the + // Illumination Properties that are turned on in the // model Property Editor // 0 Color on and Ambient off // 1 Color on and Ambient on @@ -727,7 +727,7 @@ int v3dToObj( const char *source, const char *dest, UserModifier *userModifier ) fprintf(fpMtl, "Kd 0.5 0.5 0.5\n"); // grey } fprintf(fpMtl, "illum 1\n"); - + // specify material in *.obj file fprintf(fpOut, "usemtl color_%d\n", color); textureName[0] = '\0'; @@ -745,6 +745,7 @@ int v3dToObj( const char *source, const char *dest, UserModifier *userModifier ) sscanf( lineBuffer, "%*s %s", modelType ); // We can't use only the model type as object name because a *.3d file can contain more than one "begin_model" statement with the same model type (example: begin_model rotor) fprintf( fpOut, "o obj%d_%s\n", objectNr++, modelType ); + strtoupper( modelType, (const char *) modelType ); fprintf(fpOut, "usemtl color_%d\n", color); textureName[0] = '\0'; textureOrient[0] = '\0'; @@ -754,6 +755,7 @@ int v3dToObj( const char *source, const char *dest, UserModifier *userModifier ) sscanf( lineBuffer, "%*s %s", modelType ); // We can't use only the model type as object name because a *.3d file can contain more than one "begin_model" statement with the same model type (example: begin_model rotor) fprintf( fpOut, "o obj%d_%s\n", objectNr++, modelType ); + strtoupper( modelType, (const char *) modelType ); } else if ( !strcmp(tagName, "END_MODEL") ) { modifier.tx = 0; @@ -764,9 +766,11 @@ int v3dToObj( const char *source, const char *dest, UserModifier *userModifier ) modifier.rb = 0; textureName[0] = '\0'; textureOrient[0] = '\0'; + strcpy(modelType, ""); } else if ( !strcmp(tagName, "TRANSLATE") ) { - sscanf( lineBuffer, "%*s %lf %lf %lf", &modifier.tx, &modifier.ty, &modifier.tz ); + if ( userModifier->doNotTransformModels != true ) + sscanf( lineBuffer, "%*s %lf %lf %lf", &modifier.tx, &modifier.ty, &modifier.tz ); } else if ( !strcmp(tagName, "UNTRANSLATE") || !strcmp(tagName, "END_MODEL") ) { modifier.tx = 0; @@ -774,7 +778,8 @@ int v3dToObj( const char *source, const char *dest, UserModifier *userModifier ) modifier.tz = 0; } else if ( !strcmp(tagName, "ROTATE") ) { - sscanf( lineBuffer, "%*s %lf %lf %lf", &modifier.rh, &modifier.rp, &modifier.rb ); + if ( userModifier->doNotTransformModels != true ) + sscanf( lineBuffer, "%*s %lf %lf %lf", &modifier.rh, &modifier.rp, &modifier.rb ); } else if ( !strcmp(tagName, "UNROTATE") || !strcmp(tagName, "END_MODEL") ) { modifier.rh = 0; @@ -786,13 +791,13 @@ int v3dToObj( const char *source, const char *dest, UserModifier *userModifier ) sscanf( lineBuffer, "%*s %s %s %s", textureName, texturePath, priority ); fprintf(fpMtl, "newmtl %s\n", textureName); fprintf(fpMtl, "Kd 1.000000 1.000000 1.000000\n"); - + /* fast but dirty way to modify extension */ int extensionPosition = strlen( texturePath ) - 3; texturePath[ extensionPosition++ ] = 't'; texturePath[ extensionPosition++ ] = 'g'; texturePath[ extensionPosition ] = 'a'; - + fprintf(fpMtl, "map_Kd %s\n", texturePath); textureName[0] = '\0'; textureOrient[0] = '\0'; @@ -801,7 +806,7 @@ int v3dToObj( const char *source, const char *dest, UserModifier *userModifier ) else if ( !strcmp(tagName, "TEXTURE_SELECT") ) { sscanf( lineBuffer, "%*s %s", textureName ); fprintf( fpOut, "usemtl %s\n", textureName ); - + textureOrient[0] = '\0'; } else if ( !strcmp(tagName, "TEXTURE_ORIENT_XY") || !strcmp(tagName, "TEXTURE_ORIENT_XZ") || !strcmp(tagName, "TEXTURE_ORIENT_YZ")) { @@ -820,15 +825,15 @@ int v3dToObj( const char *source, const char *dest, UserModifier *userModifier ) fprintf(fpOut, "\n"); } //while ( (c = fgetc(fpIn)) != '\n') fprintf(fpOut, "%c", c); - + } - + lineNr++; // input file line counter } printf("%s contains:\n %d points, %d lines, %d line_strip, %d line_loop, %d triangle, %d triangle_strip, %d triangle_fan, %d quads, %d quad_strip, %d polygon, and %d color statements.\n", source, points, lines, line_strip, line_loop, triangle, triangle_strip, triangle_fan, quads, quad_strip, polygon, color); - + printf("Files %s.obj and %s.mtl written to: %s directory.\n", baseName, baseName, path); - + if ( path != NULL ) { free( path ); @@ -844,7 +849,7 @@ int v3dToObj( const char *source, const char *dest, UserModifier *userModifier ) free( extension ); extension = NULL; } - + fclose(fpMtl); fclose(fpOut); fclose(fpIn);