00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include <PVLE/3D/Utility3D.h>
00028 #include <PVLE/Util/TracedException.h>
00029 #include <PVLE/Util/Math.h>
00030 #include <PVLE/Util/Util.h>
00031 #include <osgDB/ReadFile>
00032 #include <osgDB/WriteFile>
00033 #include <osg/Shape>
00034 #include <osg/Vec2>
00035 #include <limits.h>
00036
00037 #include <osg/Version>
00038 #ifndef OSG_MIN_VERSION_REQUIRED
00039
00040 #define OSG_MIN_VERSION_REQUIRED(MAJOR, MINOR, PATCH) ((OPENSCENEGRAPH_MAJOR_VERSION>MAJOR) || (OPENSCENEGRAPH_MAJOR_VERSION==MAJOR && (OPENSCENEGRAPH_MINOR_VERSION>MINOR || (OPENSCENEGRAPH_MINOR_VERSION==MINOR && OPENSCENEGRAPH_PATCH_VERSION>=PATCH))))
00041 #endif
00042
00043
00044 bool hasUpdateCallbackType(const osg::Node & node, const std::type_info & type) {
00045 const osg::NodeCallback * pCurrentCB = node.getUpdateCallback();
00046 if (!pCurrentCB) return false;
00047
00048
00049 for(; pCurrentCB; pCurrentCB = pCurrentCB->getNestedCallback()) {
00050 if (typeid(*pCurrentCB) == type) return true;
00051 }
00052 return false;
00053 }
00054
00055 float computeAngle(const osg::Vec3f & v1, const osg::Vec3f & v2, const osg::Vec3f reference) {
00056 osg::Vec3f v3(v1 ^ v2);
00057 float angle = atan2(v3.length(), v1*v2);
00058 return v3*reference < 0.f ? -angle : angle;
00059 }
00060
00061 double computeAngle(const osg::Vec3d & v1, const osg::Vec3d & v2, const osg::Vec3d reference) {
00062 osg::Vec3d v3(v1 ^ v2);
00063 double angle = atan2(v3.length(), v1*v2);
00064 return v3*reference < 0. ? -angle : angle;
00065 }
00066
00067 #include <osg/Endian>
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124 osg::Image * createNormalMap(const osg::Image* image)
00125 {
00126 if (image->r() > 1) {
00127 LOG_NOTICE << "Normal map generation for 3D textures is not supported yet." << std::endl;
00128 return NULL;
00129 }
00130 if (image->s() <= 2 || image->t() <= 2) {
00131 LOG_NOTICE << "Normal map generation needs a source which is more than 2 pixel large (or long)." << std::endl;
00132 return NULL;
00133 }
00134
00135
00136 unsigned int sourcePixelIncrement = 1;
00137 unsigned int alphaOffset = 0;
00138 switch(image->getPixelFormat())
00139 {
00140 case(GL_ALPHA):
00141 case(GL_LUMINANCE):
00142 sourcePixelIncrement = 1;
00143 alphaOffset = 0;
00144 break;
00145 case(GL_LUMINANCE_ALPHA):
00146 sourcePixelIncrement = 2;
00147 alphaOffset = 1;
00148 break;
00149 case(GL_RGB):
00150 LOG_NOTICE << "Normal map generation will only use the first encountered color channel." << std::endl;
00151 sourcePixelIncrement = 3;
00152 alphaOffset = 0;
00153 break;
00154 case(GL_RGBA):
00155 LOG_NOTICE << "Normal map generation will only use the first color channel." << std::endl;
00156 sourcePixelIncrement = 4;
00157 alphaOffset = 3;
00158 break;
00159 default:
00160 LOG_WARN << "Source pixel format not supported for normal map generation." << std::endl;
00161 return NULL;
00162 }
00163
00164 osg::Image * normalmap = new osg::Image;
00165 normalmap->allocateImage(image->s(), image->t(), image->r(), GL_RGB, GL_UNSIGNED_BYTE);
00166
00167 if (osg::getCpuByteOrder()==osg::LittleEndian) alphaOffset = sourcePixelIncrement-alphaOffset-1;
00168 const unsigned char ZERO = 0;
00169
00170
00171
00172 {
00173
00174 float angleX, angleY;
00175
00176 for(int t=0;t<image->t();++t)
00177 {
00178 const unsigned char* ptr = image->data(0,t,0)+alphaOffset;
00179 const unsigned char* left = image->data(0,t,0)+alphaOffset - sourcePixelIncrement;
00180 const unsigned char* right = image->data(1,t,0)+alphaOffset;
00181 const unsigned char* above = t==image->t()-1 ? &ZERO : image->data(0,t+1,0)+alphaOffset;
00182 const unsigned char* below = t==0 ? &ZERO : image->data(0,t-1,0)+alphaOffset;
00183
00184 unsigned char* destination = (unsigned char*) normalmap->data(0,t,0);
00185
00186
00187 for(int s=0;s<image->s();++s)
00188 {
00189 if (s==0)
00190 angleX = ( ((float)(*right)-(float)(*image->data(s+2,t,0)+alphaOffset)) / 255.f + 1.f) * osg::PI_2;
00191 else if (s==image->s()-1)
00192 angleX = ( ((float)(*image->data(s-2,t,0)+alphaOffset)-(float)(*left)) / 255.f + 1.f) * osg::PI_2;
00193 else
00194 angleX = ( ((float)(*left)-(float)(*right)) / 255.f + 1.f) * osg::PI_2;
00195 if (t==0)
00196 angleY = ( ((float)(*above)-(float)(*image->data(s,t+2,0)+alphaOffset)) / 255.f + 1.f) * osg::PI_2;
00197 else if (t==image->t()-1)
00198 angleY = ( ((float)(*image->data(s,t-2,0)+alphaOffset)-(float)(*below)) / 255.f + 1.f) * osg::PI_2;
00199 else
00200 angleY = ( ((float)(*below)-(float)(*above)) / 255.f + 1.f) * osg::PI_2;
00201
00202
00203
00204
00205
00206 osg::Vec3f grad(cos(angleX), cos(angleY), sin(angleX) * sin(angleY));
00207
00208 grad.x() = osg::clampBetween((grad.x()+1.0f)*128.0f,0.0f,255.0f);
00209 grad.y() = osg::clampBetween((grad.y()+1.0f)*128.0f,0.0f,255.0f);
00210 grad.z() = osg::clampBetween((grad.z()+1.0f)*128.0f,0.0f,255.0f);
00211
00212 *(destination++) = (unsigned char)(grad.x());
00213 *(destination++) = (unsigned char)(grad.y());
00214 *(destination++) = (unsigned char)(grad.z());
00215
00216
00217
00218
00219 ptr += sourcePixelIncrement;
00220 left += sourcePixelIncrement;
00221 right += sourcePixelIncrement;
00222 above += sourcePixelIncrement;
00223 below += sourcePixelIncrement;
00224 }
00225 }
00226 }
00227
00228 return normalmap;
00229 }
00230
00231
00233 class CartographyColorizator {
00234 public:
00235 CartographyColorizator(float valMin = 0, float valGround = 0, float valGroundI = .5f, float valMax = 1)
00236 : colWater0 (0x16/255.f, 0x77/255.f, 0xB4/255.f, 1),
00237 colWater1 (0x49/255.f, 0xA9/255.f, 0xDB/255.f, 1),
00238
00239 colGround0(.35f, .75f, .2f, 1),
00240 colGround1(.65f, .55f, .35f, 1),
00241 colGround2(1, 1, 1, 1),
00242 valMin(valMin), valGround(valGround), valGroundI(valGroundI), valMax(valMax)
00243 {
00244 ASSERT(valMin <= valGround);
00245 ASSERT(valGround <= valGroundI);
00246 ASSERT(valGroundI <= valMax);
00247 }
00248
00249
00250 void valsFromHF(const osg::HeightField * pHF) {
00251 const UINT sizeX = pHF->getNumColumns();
00252 const UINT sizeY = pHF->getNumRows();
00253 ASSERT(sizeX > 0 && sizeY > 0);
00254
00255 valMin = FLT_MAX;
00256 valGround = FLT_MAX;
00257 valGroundI = FLT_MAX;
00258 valMax = -FLT_MAX;
00259 float val;
00260
00261 for(UINT x=0; x<sizeX; ++x) {
00262 for(UINT y=0; y<sizeY; ++y) {
00263 val = pHF->getVertex(x, y).z();
00264 clampu(valMin, val);
00265 clampl(valMax, val);
00266 }
00267 }
00268 ASSERT(valMin != FLT_MAX && valMax != -FLT_MAX);
00269 valGround = valMin;
00270 valGroundI = (valGround + valMax)*.7f;
00271 }
00272
00273
00274 osg::Vec4f operator()(float val) {
00275 ASSERT(val >= valMin && val <= valMax);
00276 if (val < valGround) {
00277
00278 return colWater1;
00279 } else if (val < valGroundI) {
00280
00281 return (colGround1 - colGround0) * ((val-valGround ) / (valGroundI-valGround )) + colGround0;
00282 } else {
00283
00284 return (colGround2 - colGround1) * ((val-valGroundI) / (valMax -valGroundI)) + colGround1;
00285 }
00286 }
00287
00288
00289 const osg::Vec4f colWater0;
00290 const osg::Vec4f colWater1;
00291 const osg::Vec4f colGround0;
00292 const osg::Vec4f colGround1;
00293 const osg::Vec4f colGround2;
00294
00295 protected:
00296 float valMin, valGround, valGroundI, valMax;
00297 };
00298
00299
00300 osg::Image * createHeightColorization(const osg::HeightField * pHF) {
00301 const UINT sizeX = pHF->getNumColumns();
00302 const UINT sizeY = pHF->getNumRows();
00303 ASSERT(sizeX > 0 && sizeY > 0);
00304
00305 osg::Image * pImage = new osg::Image;
00306 pImage->allocateImage(sizeX, sizeY, 1, GL_RGB, GL_UNSIGNED_BYTE);
00307 UCHAR * pDest = pImage->data();
00308
00309 CartographyColorizator cc;
00310 cc.valsFromHF(pHF);
00311 osg::Vec4f color;
00312 for(UINT x=0; x<sizeX; ++x) {
00313 for(UINT y=0; y<sizeY; ++y) {
00314 color = cc(pHF->getVertex(y, x).z());
00315
00316 clamp(color.x(), 0.f, 1.f);
00317 clamp(color.y(), 0.f, 1.f);
00318 clamp(color.z(), 0.f, 1.f);
00319 clamp(color.w(), 0.f, 1.f);
00320 *(pDest++) = static_cast<UCHAR>(color.x()*255);
00321 *(pDest++) = static_cast<UCHAR>(color.y()*255);
00322 *(pDest++) = static_cast<UCHAR>(color.z()*255);
00323 }
00324 }
00325 return pImage;
00326 }
00327
00328 #include <osg/TexGen>
00329 #include <osg/Texture1D>
00330 #include <osg/Material>
00331
00332 osg::StateSet* create1DTextureHeightColorization(const osg::HeightField * pHF)
00333 {
00334 osg::Image* image = new osg::Image;
00335 int noPixels = 512;
00336
00337
00338 image->allocateImage(noPixels,1,1,GL_RGBA,GL_FLOAT);
00339 image->setInternalTextureFormat(GL_RGBA);
00340
00341
00342 CartographyColorizator cc;
00343 osg::Vec4* dataPtr = (osg::Vec4*)image->data();
00344 for(int i=0;i<noPixels;++i) {
00345 *(dataPtr++) = cc(i/static_cast<float>(noPixels-1));
00346 }
00347
00348 osg::Texture1D* texture = new osg::Texture1D;
00349
00350 texture->setWrap(osg::Texture1D::WRAP_S,osg::Texture1D::CLAMP_TO_EDGE);
00351 texture->setFilter(osg::Texture1D::MIN_FILTER,osg::Texture1D::LINEAR);
00352 texture->setFilter(osg::Texture1D::MAG_FILTER,osg::Texture1D::LINEAR);
00353 texture->setImage(image);
00354
00355 float valMin = FLT_MAX;
00356 float valMax = -FLT_MAX;
00357 float val;
00358 const UINT sizeX = pHF->getNumColumns();
00359 const UINT sizeY = pHF->getNumRows();
00360 ASSERT(sizeX > 0 && sizeY > 0);
00361 for(UINT x=0; x<sizeX; ++x) {
00362 for(UINT y=0; y<sizeY; ++y) {
00363 #if OSG_MIN_VERSION_REQUIRED(2,9,0)
00364 val = pHF->getVertex(x, y).z();
00365 #else
00366 val = pHF->getHeight(x, y);
00367 #endif
00368 clampu(valMin, val);
00369 clampl(valMax, val);
00370 }
00371 }
00372
00373 osg::TexGen* texgen = new osg::TexGen;
00374 texgen->setMode(osg::TexGen::OBJECT_LINEAR);
00375 float scale = 1/(valMax-valMin);
00376
00377
00378 texgen->setPlane(osg::TexGen::S, osg::Plane(0,0, scale, .5f-(valMin+valMax)/2*scale));
00379
00380 osg::Material* material = new osg::Material;
00381 material->setDiffuse(osg::Material::FRONT, osg::Vec4(1,1,1,1));
00382 material->setAmbient(osg::Material::FRONT, osg::Vec4(.1f,.1f,.1f,1));
00383 material->setSpecular(osg::Material::FRONT, osg::Vec4(.1f,.1f,.1f,1));
00384
00385
00386 osg::StateSet* stateset = new osg::StateSet;
00387 stateset->setTextureAttribute(0,texture);
00388 stateset->setTextureMode(0,GL_TEXTURE_1D,osg::StateAttribute::ON);
00389 stateset->setTextureMode(0,GL_TEXTURE_2D,osg::StateAttribute::OFF);
00390 stateset->setTextureMode(0,GL_TEXTURE_3D,osg::StateAttribute::OFF);
00391
00392 stateset->setTextureAttribute(0,texgen);
00393 stateset->setTextureMode(0,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
00394
00395 stateset->setAttribute(material);
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407 return stateset;
00408 }
00409
00410
00411 void spotLightProcessing(osg::Image * pImage, float cutoff, float exponent, float constantAttenuation, float linearAttenuation, float quadraticAttenuation, float distFactor) {
00412 ASSERT(pImage->getPixelFormat() == GL_RGBA);
00413 ASSERT(pImage->r() == 1);
00414 ASSERT(cutoff>=0 && cutoff<=90);
00415 cutoff = osg::DegreesToRadians(cutoff);
00416
00417
00418 unsigned int alphaOffset = 0;
00419 switch(pImage->getPixelFormat())
00420 {
00421 case(GL_ALPHA):
00422 case(GL_LUMINANCE):
00423 alphaOffset = 0;
00424 break;
00425 case(GL_LUMINANCE_ALPHA):
00426 alphaOffset = 1;
00427 break;
00428 case(GL_RGBA):
00429 alphaOffset = 3;
00430 break;
00431 default:
00432 LOG_NOTICE << "Source pixel format not supported for spotlight map generation." << std::endl;
00433 return;
00434 }
00435
00436 osg::Image * normalmap = new osg::Image;
00437
00438
00439
00440
00441
00442 osg::Vec2f center((pImage->s()+1)/2, 0);
00443 float dist, cosAngle, angle;
00444 distFactor /= pImage->t();
00445 for(int t=0;t<pImage->t();++t)
00446 {
00447 unsigned char* destination = pImage->data(0,t,0);
00448 for(int s=0;s<pImage->s();++s) {
00449 osg::Vec2f pos(s, t);
00450 osg::Vec2f dir(pos - center);
00451 dist = dir.length();
00452 if (dist > 0) {
00453 cosAngle = (dir/dist) * osg::Vec2f(0,1);
00454 angle = acosf(cosAngle);
00455 } else {
00456 cosAngle = 1;
00457 angle = 0;
00458 }
00459 ASSERT(angle>=0);
00460
00461 destination += alphaOffset;
00462 if (angle > cutoff)
00463 *destination = 0;
00464 else {
00465 dist *= distFactor;
00466 *destination = static_cast<UCHAR>( clamp_copy(
00467 1/(constantAttenuation + linearAttenuation * dist + quadraticAttenuation * dist*dist)
00468 * powf(cosAngle, exponent)
00469 , 0.f, 1.f) * 255.f );
00470 }
00471 destination += pImage->getPixelSizeInBits()/8-alphaOffset;
00472 }
00473 }
00474 }
00475
00476
00477 osg::GraphicsContext::ScreenSettings getClosestFullscreenResolution(const osg::GraphicsContext::ScreenIdentifier & si, const osg::GraphicsContext::ScreenSettings & resolution) {
00478 osg::GraphicsContext::WindowingSystemInterface *wsi = osg::GraphicsContext::getWindowingSystemInterface();
00479 ASSERT(wsi);
00480 osg::GraphicsContext::ScreenSettingsList list;
00481 wsi->enumerateScreenSettings(si, list);
00482
00483
00484 int bestX=0, bestY=0, bestDelta=INT_MAX, curDelta;
00485 BOOST_FOREACH(const osg::GraphicsContext::ScreenSettings & curRes, list) {
00486 if (curRes.colorDepth < resolution.colorDepth) continue;
00487 if (curRes.width == resolution.width && curRes.height == resolution.height) {
00488
00489 if (curRes.colorDepth == resolution.colorDepth && curRes.colorDepth == resolution.colorDepth)
00490 return resolution;
00491 bestX = curRes.width;
00492 bestY = curRes.height;
00493 break;
00494 } else {
00495 curDelta = abs(curRes.width - resolution.width) + abs(curRes.height - resolution.height);
00496 if (curDelta < bestDelta || (curDelta == bestDelta && bestX && bestY && curRes.width > bestX && curRes.height > bestY)) {
00497
00498 bestDelta = curDelta;
00499 bestX = curRes.width;
00500 bestY = curRes.height;
00501 }
00502 }
00503 }
00504 if (bestX==0 || bestY==0) {
00505 LOG_NOTICE << "Can't find fullscreen resolution close to " << resolution << ".";
00506 return osg::GraphicsContext::ScreenSettings();
00507 }
00508
00509
00510 UINT bestDepth=0, bestDepthDelta=UINT_MAX, curDepthDelta;
00511 BOOST_FOREACH(const osg::GraphicsContext::ScreenSettings & curRes, list) {
00512 if (curRes.colorDepth < resolution.colorDepth) continue;
00513 if (curRes.width != bestX || curRes.height != bestY) continue;
00514 curDepthDelta = abs(static_cast<int>(curRes.colorDepth) - static_cast<int>(resolution.colorDepth));
00515 if (curDepthDelta == 0) {
00516
00517 bestDepth = curRes.colorDepth;
00518 break;
00519 } else {
00520 if (curDepthDelta < bestDepthDelta || (curDepthDelta == bestDepthDelta && bestDepth && curRes.colorDepth > bestDepth)) {
00521
00522 bestDepthDelta = curDepthDelta;
00523 bestDepth = curRes.colorDepth;
00524 }
00525 }
00526 }
00527 if (bestDepth==0) {
00528 LOG_NOTICE << "Can't find fullscreen resolution close to " << resolution << ".";
00529 return osg::GraphicsContext::ScreenSettings();
00530 }
00531
00532
00533 double bestRate=0;
00534 const osg::GraphicsContext::ScreenSettings * pBest = NULL;
00535 BOOST_FOREACH(const osg::GraphicsContext::ScreenSettings & curRes, list) {
00536 if (curRes.colorDepth < resolution.colorDepth) continue;
00537 if (curRes.width != bestX || curRes.height != bestY) continue;
00538 if (curRes.colorDepth != bestDepth) continue;
00539 if (curRes.refreshRate > bestRate) {
00540 bestRate = curRes.refreshRate;
00541 pBest = &curRes;
00542 }
00543 }
00544 if (!pBest) {
00545 LOG_NOTICE << "Can't find fullscreen resolution close to " << resolution << ".";
00546 return osg::GraphicsContext::ScreenSettings();
00547 }
00548
00549 return *pBest;
00550 }
00551
00552
00553 std::ostream& operator<<(std::ostream& stream, const osg::GraphicsContext::ScreenSettings& settings) {
00554 stream << settings.width << "x" << settings.height << ", " << settings.refreshRate << " Hz, " << settings.colorDepth << " bits";
00555 return stream;
00556 }
00557
00558
00559 namespace osgDB {
00560 osg::Image * readImageFile(const boost::filesystem::path & path, const osgDB::ReaderWriter::Options* options) {
00561 osg::Image * p = osgDB::readImageFile(path.native_file_string(), options);
00562 if (!p) THROW_TRACED_EXCEPTION("Could not load " + path.string());
00563 return p;
00564 }
00565 osg::Image * readImageFile(const boost::filesystem::path & path) {
00566 osg::Image * p = osgDB::readImageFile(path.native_file_string());
00567 if (!p) THROW_TRACED_EXCEPTION("Could not load " + path.string());
00568 return p;
00569 }
00570
00571 void writeImageFile(const osg::Image & image, const boost::filesystem::path & path) {
00572 if (!osgDB::writeImageFile(image, path.native_file_string())) THROW_TRACED_EXCEPTION("Could not write " + path.string());
00573 }
00574
00575 osg::Node * readNodeFile(const boost::filesystem::path & path, const osgDB::ReaderWriter::Options* options) {
00576 osg::Node * p = osgDB::readNodeFile(path.native_file_string(), options);
00577 if (!p) THROW_TRACED_EXCEPTION("Could not load " + path.string());
00578 return p;
00579 }
00580
00581 osg::Node * readNodeFile(const boost::filesystem::path & path) {
00582 osg::Node * p = osgDB::readNodeFile(path.native_file_string());
00583 if (!p) THROW_TRACED_EXCEPTION("Could not load " + path.string());
00584 return p;
00585 }
00586
00587 void writeNodeFile(const osg::Node & node, const boost::filesystem::path & path) {
00588 if (!osgDB::writeNodeFile(node, path.native_file_string())) THROW_TRACED_EXCEPTION("Could not write " + path.string());
00589 }
00590
00591 }