# Copyright 2007, Linden Research, Inc. # License: GPL - http://www.gnu.org/copyleft/gpl.html # Index: indra/newview/llviewermenufile.cpp =================================================================== --- indra/newview/llviewermenufile.cpp (revision 64) +++ indra/newview/llviewermenufile.cpp (working copy) @@ -532,6 +532,21 @@ return; } } + else if( LLString::compareInsensitive(ext.c_str(),".obj") == 0 ) + { + asset_type = LLAssetType::AT_TEXTURE; + if (!LLViewerImageList::createUploadFile(src_filename, + filename, + IMG_CODEC_OBJ )) + { + snprintf(error_message, MAX_STRING, "Problem with file %s:\n\n%s\n", /* Flawfinder: ignore */ + src_filename.c_str(), LLImageBase::getLastError().c_str()); + args["[FILE]"] = src_filename; + args["[ERROR]"] = LLImageBase::getLastError(); + upload_error(error_message, "ProblemWithFile", filename, args); + return; + } + } else if( LLString::compareInsensitive(ext.c_str(),".tga") == 0 ) { asset_type = LLAssetType::AT_TEXTURE; Index: indra/newview/llfloaterimagepreview.cpp =================================================================== --- indra/newview/llfloaterimagepreview.cpp (revision 64) +++ indra/newview/llfloaterimagepreview.cpp (working copy) @@ -34,6 +34,7 @@ #include "llimagetga.h" #include "llimagejpeg.h" #include "llimagepng.h" +#include "llimageobj.h" #include "llagent.h" #include "llbutton.h" @@ -330,6 +331,10 @@ { codec = IMG_CODEC_PNG; } + else if( 0 == strnicmp(ext, ".obj", 4) ) + { + codec = IMG_CODEC_OBJ; + } LLPointer raw_image = new LLImageRaw; @@ -402,6 +407,21 @@ } } break; + case IMG_CODEC_OBJ: + { + LLPointer obj_image = new LLImageOBJ; + + if (!obj_image->load(src_filename)) + { + return false; + } + + if (!obj_image->decode(raw_image)) + { + return false; + } + } + break; default: return false; } Index: indra/newview/llviewermenu.cpp =================================================================== --- indra/newview/llviewermenu.cpp (revision 64) +++ indra/newview/llviewermenu.cpp (working copy) @@ -244,7 +244,7 @@ #if LL_WINDOWS static const char* SOUND_EXTENSIONS = ".wav"; -static const char* IMAGE_EXTENSIONS = ".tga .bmp .jpg .jpeg .png"; +static const char* IMAGE_EXTENSIONS = ".tga .bmp .jpg .jpeg .png .obj"; static const char* ANIM_EXTENSIONS = ".bvh"; #ifdef _CORY_TESTING static const char* GEOMETRY_EXTENSIONS = ".slg"; Index: indra/newview/llfilepicker.cpp =================================================================== --- indra/newview/llfilepicker.cpp (revision 64) +++ indra/newview/llfilepicker.cpp (working copy) @@ -49,7 +49,7 @@ #if LL_WINDOWS #define SOUND_FILTER L"Sounds (*.wav)\0*.wav\0" -#define IMAGE_FILTER L"Images (*.tga; *.bmp; *.jpg; *.jpeg; *.png)\0*.tga;*.bmp;*.jpg;*.jpeg;*.png\0" +#define IMAGE_FILTER L"Images (*.tga; *.bmp; *.jpg; *.jpeg; *.png; *.obj)\0*.tga;*.bmp;*.jpg;*.jpeg;*.png;*.obj\0" #define ANIM_FILTER L"Animations (*.bvh)\0*.bvh\0" #ifdef _CORY_TESTING #define GEOMETRY_FILTER L"SL Geometry (*.slg)\0*.slg\0" @@ -511,11 +511,12 @@ if (fileInfo.filetype != 'JPEG' && fileInfo.filetype != 'JPG ' && fileInfo.filetype != 'BMP ' && fileInfo.filetype != 'TGA ' && fileInfo.filetype != 'BMPf' && fileInfo.filetype != 'TPIC' && - fileInfo.filetype != 'PNG ' && + fileInfo.filetype != 'PNG ' && fileInfo.filetype != 'OBJ ' && (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("jpeg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && CFStringCompare(fileInfo.extension, CFSTR("jpg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && CFStringCompare(fileInfo.extension, CFSTR("bmp"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && CFStringCompare(fileInfo.extension, CFSTR("tga"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && + CFStringCompare(fileInfo.extension, CFSTR("obj"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && CFStringCompare(fileInfo.extension, CFSTR("png"), kCFCompareCaseInsensitive) != kCFCompareEqualTo)) ) { @@ -1171,7 +1172,7 @@ case FFLOAD_ANIM: caption += "Animations (*.bvh)"; break; case FFLOAD_IMAGE: - caption += "Images (*.tga; *.bmp; *.jpg; *.jpeg; *.png)"; break; + caption += "Images (*.tga; *.bmp; *.jpg; *.jpeg; *.png; *.obj)"; break; default:; break; } Index: indra/newview/llviewerimagelist.cpp =================================================================== --- indra/newview/llviewerimagelist.cpp (revision 64) +++ indra/newview/llviewerimagelist.cpp (working copy) @@ -39,6 +39,7 @@ #include "llimagetga.h" #include "llimagejpeg.h" #include "llimagepng.h" +#include "llimageobj.h" #include "llmediaengine.h" #include "llsdserialize.h" @@ -1096,6 +1097,21 @@ } } break; + case IMG_CODEC_OBJ: + { + LLPointer obj_image = new LLImageOBJ; + + if (!obj_image->load(filename)) + { + return FALSE; + } + + if (!obj_image->decode(raw_image)) + { + return FALSE; + } + } + break; default: return FALSE; } Index: indra/llimage/llimageobj.cpp =================================================================== --- indra/llimage/llimageobj.cpp (revision 0) +++ indra/llimage/llimageobj.cpp (revision 0) @@ -0,0 +1,259 @@ +/** + * @file llimagebmp.cpp + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "linden_common.h" + +#include "llimagebmp.h" +#include "llimageobj.h" +#include "llerror.h" +#include "lldarray.h" +#include "v3math.h" + +#include +#include +#include +#include + +struct LLImageOBJFacePoint +{ + U32 vertex, texture, normal; +}; + +struct LLImageOBJFace +{ + std::vector index; +}; + + +BOOL LLImageOBJ::updateData() +{ + return TRUE; +} + + +static void decodeVertex2RGB( U8* dst, LLVector3 p, LLVector3& min, LLVector3& max) +{ + p -= min; + dst[VX] = (U8)llround( p.mV[VX] / max.mV[VX] * 255.0f ); + dst[VY] = (U8)llround( p.mV[VY] / max.mV[VY] * 255.0f ); + dst[VZ] = (U8)llround( p.mV[VZ] / max.mV[VZ] * 255.0f ); +} + + +BOOL LLImageOBJ::decode(LLImageRaw* raw_image, F32 decode_time) +{ + resetLastError(); + + // Check to make sure that this instance has been initialized with data + U8* mdata = getData(); + if (!mdata || (0 == getDataSize())) + { + setLastError("Uninitialized instance of LLImageOBJ"); + return FALSE; + } + + std::string copy_of_data((char*)mdata, getDataSize()); + std::basic_istringstream istr(copy_of_data); + + std::vector vertices; + std::vector normals; + std::vector textures; // similar to uv map + std::vector faces; // triangular or polygonic indexes to vertices, normals, & textures + + while( istr.good() ) + { + switch( istr.get() ) + { + case '\n': continue; + case 'v': + { + char n = istr.get(); + LLVector3 v; + if( n == ' ' ) + { + istr >> v.mV[VX] >> v.mV[VY] >> v.mV[VZ]; + if(istr.fail()) + { + setLastError("Invalid vertex data in OBJ file"); + return FALSE; + } + vertices.push_back(v); + } + else + if( n == 'n' ) + { + istr >> v.mV[VX] >> v.mV[VY] >> v.mV[VZ]; + if(istr.fail()) + { + setLastError("Invalid vertex normal data in OBJ file"); + return FALSE; + } + normals.push_back(v); + } + else + if( n == 't' ) + { + istr >> v.mV[VX] >> v.mV[VY]; + if(istr.fail()) + { + setLastError("Invalid vertex texture data in OBJ file"); + return FALSE; + } + v.mV[VZ] = 1.0; + textures.push_back(v); + } + break; + } + case 'f': // face indices + { + LLImageOBJFace f; + while( istr.good() ) + { + char c = istr.get(); + if( c == '\r' ) + c = istr.get(); + if( c == '\n' ) + break; + LLImageOBJFacePoint p; + istr >> p.vertex; + if( istr.fail() ) + { + setLastError("Invalid face vertex index data in OBJ file"); + return FALSE; + } + p.texture = 0; + p.normal = 0; + if( istr.get() != '/' ) + { + istr.unget(); + f.index.push_back(p); + continue; + } + c = istr.get(); + if( '0' <= c && c <= '9' ) + { + istr.unget(); + istr >> p.texture; + if( istr.fail() ) + { + setLastError("Invalid face texture index data in OBJ file"); + return FALSE; + } + c = istr.get(); + } + if( c != '/' ) + { + istr.unget(); + f.index.push_back(p); + continue; + } + istr >> p.normal; + if( istr.fail() ) + { + setLastError("Invalid face normal index data in OBJ file"); + return FALSE; + } + f.index.push_back(p); + } + faces.push_back(f); + } + case '#': // # - comment + case 'o': // o - object name + case 'g': // g - group + case 'm': // mtllib - material lib + case 'l': // ll - line + case 'u': // usemtl - use material + default: + { + while( istr.good() && istr.get() != '\n' ); + } + } + } + if( istr.bad() ) + { + setLastError("Unable to read OBJ data"); + return FALSE; + } + + if( 3 > vertices.size() ) + { + setLastError("OBJ file lacks vertex data"); + return FALSE; + } + + // *NOTE: we assume normals exists in the data, but we could generate them + // based on the standard for OBJ files being a model that has all convex faces by default + //if( 0 == normals.size() ) + //{ + // setLastError("OBJ file lacks vertex normal data"); + // return FALSE; + //} + //if( vertices.size() != normals.size() ) + //{ + // setLastError("Unsupported vertex normal topology"); + // return FALSE; + //} + + // *NOTE: We could do arbitrary vertices, but for now we use a + // default method for straight-forward 64x64 covex-face spheric topology conversion + // to retain intended sharpness + // this may be different for more cylindric topology, or those that lack a polar vertex + if( 3970 != vertices.size() ) // 3970 = (64 rows - 1 (overlap)) * (64 cols - 1 (overlap)) + 1 pole ? + { + setLastError("Conversion requires a 64x64 point spheric mesh"); + return FALSE; + } + + raw_image->resize(64, 64, 3); + + // *NOTE: We could, instead, trace the faces and determine if a spheric topology is possible. + // Then, mutate non-convex areas to proportional locations on a new sphere, + // which would create a new uv-index map and avoid subsequent ray-casts for those areas. + // For now, we don't use face data. + + // assume we don't have to perform a model rotation + + // assume we don't have to perform a model diagonal flip + + // determine scale + LLVector3 min, max; + min = max = vertices[0]; + for(U32 i = 1; i < vertices.size(); ++i) + update_min_max(min,max,vertices[i]); + max -= min; + + // use default uv assumption: vertex and texture indices match. + // we could implement an auto-rotate/flip/adjust model to texture alignment. + U8* dst = raw_image->getData(); + for(U32 u = 0; u < 63; ++u) + { + for(U32 v = 0; v < 63; ++v) + { + decodeVertex2RGB( dst, vertices[u*63+v], min, max); + dst += 3; + } + decodeVertex2RGB( dst, vertices[u*63+0], min, max); + dst += 3; + } + for(U32 u = 0, v = 0; v < 63; ++v) + { + decodeVertex2RGB( dst, vertices[u*63+v], min, max); + dst += 3; + } + decodeVertex2RGB( dst, vertices[0*63+63+1], min, max); // ? + + return TRUE; +} + + +BOOL LLImageOBJ::encode(const LLImageRaw* raw_image, F32 encode_time) +{ + resetLastError(); + + setLastError("llimageobj attempted to encode raw image!"); + return FALSE; +} Index: indra/llimage/llimageobj.h =================================================================== --- indra/llimage/llimageobj.h (revision 0) +++ indra/llimage/llimageobj.h (revision 0) @@ -0,0 +1,26 @@ +/** + * @file llimageobj.h + * @brief Image implementation for OBJ. + * + * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LL_LLIMAGEOBJ_H +#define LL_LLIMAGEOBJ_H + +#include "llimage.h" + +// This class blindy converts OBJ files to a sculpt map image for preview & upload + +class LLImageOBJ : public LLImageFormatted +{ +public: + LLImageOBJ() : LLImageFormatted(IMG_CODEC_OBJ) {}; + + /*virtual*/ BOOL updateData(); + /*virtual*/ BOOL decode(LLImageRaw* raw_image, F32 time=0.0); + /*virtual*/ BOOL encode(const LLImageRaw* raw_image, F32 time=0.0); +}; + +#endif Index: indra/llimage/llimage.cpp =================================================================== --- indra/llimage/llimage.cpp (revision 64) +++ indra/llimage/llimage.cpp (working copy) @@ -47,6 +47,7 @@ #include "llimagejpeg.h" #endif #include "llimagedxt.h" +#include "llimageobj.h" //--------------------------------------------------------------------------- // LLImageBase @@ -1106,7 +1107,8 @@ { "jpeg", IMG_CODEC_JPEG }, { "mip", IMG_CODEC_DXT }, { "dxt", IMG_CODEC_DXT }, - { "png", IMG_CODEC_PNG } + { "png", IMG_CODEC_PNG }, + { "obj", IMG_CODEC_OBJ } }; #define NUM_FILE_EXTENSIONS sizeof(file_extensions)/sizeof(file_extensions[0]) Index: indra/llimage/llimage.h =================================================================== --- indra/llimage/llimage.h (revision 64) +++ indra/llimage/llimage.h (working copy) @@ -68,7 +68,8 @@ IMG_CODEC_JPEG = 5, IMG_CODEC_DXT = 6, IMG_CODEC_PNG = 7, - IMG_CODEC_EOF = 8 + IMG_CODEC_OBJ = 8, + IMG_CODEC_EOF = 9 }; //============================================================================ Index: indra/llimage/files.lst =================================================================== --- indra/llimage/files.lst (revision 64) +++ indra/llimage/files.lst (working copy) @@ -3,6 +3,7 @@ llimage/llimagedxt.cpp llimage/llimagej2c.cpp llimage/llimagejpeg.cpp +llimage/llimageobj.cpp llimage/llimagepng.cpp llimage/llimagetga.cpp llimage/llimageworker.cpp