/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield * * This application is open source and may be redistributed and/or modified * freely and without restriction, both in commericial and non commericial applications, * as long as this copyright notice is maintained. * * This application is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include float VERTICAL_SIZE = 0.0005; class GraphicsContext { public: GraphicsContext() { rs = new Producer::RenderSurface; rs->setWindowRectangle(0,0,1,1); rs->useBorder(false); rs->useConfigEventThread(false); rs->realize(); std::cout<<"Realized window"< rs; }; osg::Image* createNormalMap(osg::HeightField* grid) { osg::Image* image = new osg::Image; image->allocateImage(grid->getNumColumns(),grid->getNumRows(),1,GL_RGB,GL_BYTE); char* ptr = (char*) image->data(); for(unsigned int r=0;rgetNumRows();++r) { for(unsigned int c=0;cgetNumColumns();++c) { osg::Vec3 normal = grid->getNormal(c,r); (*ptr++) = (char)((normal.x()+1.0)*0.5*255); (*ptr++) = (char)((normal.y()+1.0)*0.5*255); (*ptr++) = (char)((normal.z()+1.0)*0.5*255); } } return image; } template< typename T> osg::HeightField* _createHeightField(osg::Image* image, float zScale) { unsigned char* ptr = image->data(); unsigned int rowSizeInBytes = image->getRowSizeInBytes(); unsigned int pixelSizeInBits = image->getPixelSizeInBits(); unsigned int pixelSizeInBytes = pixelSizeInBits/8; unsigned int numColumns = image->s(); unsigned int numRows = image->t(); osg::HeightField* grid = new osg::HeightField; grid->allocateGrid(numColumns,numRows); for(unsigned int r=0;rsetHeight(c,r,zScale*(float)(*((T*)ptr_in_row))); ptr_in_row += pixelSizeInBytes; } ptr += rowSizeInBytes; } return grid; } osg::HeightField* createHeightField(osg::Image* image, const osg::Vec3& origin, const osg::Vec3& size) { osg::HeightField* grid = new osg::HeightField; switch(image->getDataType()) { case(GL_UNSIGNED_BYTE): grid = _createHeightField(image,size.z()); break; case(GL_BYTE): grid = _createHeightField(image,size.z()); break; case(GL_UNSIGNED_SHORT): grid = _createHeightField(image,size.z()); break; case(GL_SHORT): grid = _createHeightField(image,size.z()); break; case(GL_UNSIGNED_INT): grid = _createHeightField(image,size.z()); break; case(GL_INT): grid = _createHeightField(image,size.z()); break; case(GL_FLOAT): grid = _createHeightField(image,size.z()); break; } grid->setOrigin(origin); grid->setXInterval(size.x()/(float)(image->s()-1)); grid->setYInterval(size.y()/(float)(image->s()-1)); return grid; } osg::Node* createTile(osg::HeightField* grid, unsigned int columnBegin, unsigned int columnEnd, unsigned int rowBegin, unsigned int rowEnd, float xTexCoordBegin, float xTexCoordEnd, float yTexCoordBegin, float yTexCoordEnd, unsigned int targetNumPolygonsPerTile) { int numPolys = (columnEnd-columnBegin)*(rowEnd-rowBegin)*2; int numColumns, numRows; bool oneToOneMapping = numPolys<=targetNumPolygonsPerTile; if (oneToOneMapping) { numColumns = (columnEnd-columnBegin)+1; numRows = (rowEnd-rowBegin)+1; } else { numColumns = (int)sqrtf((float)targetNumPolygonsPerTile/2.0) + 1; numRows = targetNumPolygonsPerTile/(2*(numColumns)-1) + 1; } bool createSkirt = true; int numVerticesInBody = numColumns*numRows; int numVerticesInSkirt = createSkirt ? numColumns*2 + numRows*2 - 4 : 0; int numVertices = numVerticesInBody+numVerticesInSkirt; osg::Vec3 skirtVector(0.0f,0.0f,-0.003f); osg::Geometry* geometry = new osg::Geometry; osg::Vec3Array& v = *(new osg::Vec3Array(numVertices)); osg::Vec2Array& t = *(new osg::Vec2Array(numVertices)); osg::UByte4Array& color = *(new osg::UByte4Array(1)); color[0].set(255,255,255,255); int vi=0; int r,c; if (!oneToOneMapping) { for(r=0;rgetOrigin()+osg::Vec3(grid->getXInterval()*(float)col, grid->getYInterval()*(float)row, grid->getHeight(col,row)); t[vi].x() = xTexCoordBegin + (xTexCoordEnd-xTexCoordBegin)*(float)(col-columnBegin)/(float)(columnEnd-columnBegin); t[vi].y() = yTexCoordBegin + (yTexCoordEnd-yTexCoordBegin)*(float)(row-rowBegin)/(float)(rowEnd-rowBegin); ++vi; } } } else { for(r=rowBegin;r<=rowEnd;++r) { for(c=columnBegin;c<=columnEnd;++c) { v[vi] = grid->getOrigin()+osg::Vec3(grid->getXInterval()*c, grid->getYInterval()*r, grid->getHeight(c,r)); t[vi].x() = xTexCoordBegin + (xTexCoordEnd-xTexCoordBegin)*(float)(c-columnBegin)/(float)(columnEnd-columnBegin); t[vi].y() = yTexCoordBegin + (yTexCoordEnd-yTexCoordBegin)*(float)(r-rowBegin)/(float)(rowEnd-rowBegin); ++vi; } } } geometry->setVertexArray(&v); geometry->setColorArray(&color); geometry->setColorBinding(osg::Geometry::BIND_OVERALL); geometry->setTexCoordArray(0,&t); geometry->setTexCoordArray(1,&t); bool pickOutDiagonals = true; if (pickOutDiagonals) { osg::DrawElementsUShort& drawElements = *(new osg::DrawElementsUShort(GL_TRIANGLES,2*3*(numColumns-1)*(numRows-1))); geometry->addPrimitiveSet(&drawElements); int ei=0; for(r=0;raddPrimitiveSet(&drawElements); int ei=0; for(c=0;c0) { osg::DrawElementsUShort& skirtDrawElements = *(new osg::DrawElementsUShort(GL_QUAD_STRIP,2*numVerticesInSkirt+2)); geometry->addPrimitiveSet(&skirtDrawElements); int ei=0; int firstSkirtVertexIndex = vi; // create bottom skirt vertices r=0; for(c=0;c0;--c) { skirtDrawElements[ei++] = (r)*numColumns+c; skirtDrawElements[ei++] = vi; v[vi] = v[(r)*numColumns+c]+skirtVector; t[vi++] = t[(r)*numColumns+c]; } // create left skirt vertices c=0; for(r=numRows-1;r>0;--r) { skirtDrawElements[ei++] = (r)*numColumns+c; skirtDrawElements[ei++] = vi; v[vi] = v[(r)*numColumns+c]+skirtVector; t[vi++] = t[(r)*numColumns+c]; } skirtDrawElements[ei++] = 0; skirtDrawElements[ei++] = firstSkirtVertexIndex; } osgUtil::TriStripVisitor tsv; tsv.stripify(*geometry); // // osgUtil::SmoothingVisitor sv; // sv.smooth(*geometry); osg::Vec4Array& tsgTangentArray = *(new osg::Vec4Array(1)); osg::Vec4Array& tsgBinormalArray = *(new osg::Vec4Array(1)); osg::Vec4Array& tsgNormalArray = *(new osg::Vec4Array(1)); tsgTangentArray[0].set(1.0f,0.0f,0.0f,0.0f); tsgBinormalArray[0].set(0.0f,1.0f,0.0f,0.0f); tsgNormalArray[0].set(0.0f,0.0f,1.0f,0.0f); geometry->setVertexAttribData(6, osg::Geometry::ArrayData(&tsgTangentArray, osg::Geometry::BIND_OVERALL,GL_FALSE)); geometry->setVertexAttribData(7, osg::Geometry::ArrayData(&tsgBinormalArray, osg::Geometry::BIND_OVERALL,GL_FALSE)); geometry->setVertexAttribData(15, osg::Geometry::ArrayData(&tsgNormalArray, osg::Geometry::BIND_OVERALL,GL_FALSE)); //geometry->setUseVertexBufferObjects(true); geometry->setUseDisplayList(false); geometry->setUseVertexBufferObjects(false); osg::Geode* geode = new osg::Geode; geode->addDrawable(geometry); return geode; } osg::Node* createQuadTree(osg::HeightField* grid, unsigned int columnBegin, unsigned int columnEnd, unsigned int rowBegin, unsigned int rowEnd, float xTexCoordBegin, float xTexCoordEnd, float yTexCoordBegin, float yTexCoordEnd, unsigned int targetNumPolygonsPerTile, unsigned int numLevels) { unsigned int numPolys = (columnEnd-columnBegin)*(rowEnd-rowBegin)*2; std::cout << "createQuadTree "<getBound().radius()*4.0f; float max_visible_distance = 1e7; unsigned int columnCenter = (columnBegin + columnEnd)/2; unsigned int rowCenter = (rowBegin + rowEnd)/2; float xTexCoordCenter = xTexCoordBegin+ (xTexCoordEnd-xTexCoordBegin)*((float)(columnCenter-columnBegin)/(float)(columnEnd-columnBegin)); float yTexCoordCenter = yTexCoordBegin+ (yTexCoordEnd-yTexCoordBegin)*((float)(rowCenter-rowBegin)/(float)(rowEnd-rowBegin)); osg::Group* group = new osg::Group; group->addChild(createQuadTree(grid, columnBegin, columnCenter, rowBegin, rowCenter, xTexCoordBegin, xTexCoordCenter, yTexCoordBegin, yTexCoordCenter, targetNumPolygonsPerTile, numLevels-1)); group->addChild(createQuadTree(grid, columnCenter, columnEnd, rowBegin, rowCenter, xTexCoordCenter, xTexCoordEnd, yTexCoordBegin, yTexCoordCenter, targetNumPolygonsPerTile, numLevels-1)); group->addChild(createQuadTree(grid, columnCenter, columnEnd, rowCenter, rowEnd, xTexCoordCenter, xTexCoordEnd, yTexCoordCenter, yTexCoordEnd, targetNumPolygonsPerTile, numLevels-1)); group->addChild(createQuadTree(grid, columnBegin, columnCenter, rowCenter, rowEnd, xTexCoordBegin, xTexCoordCenter, yTexCoordCenter, yTexCoordEnd, targetNumPolygonsPerTile, numLevels-1)); osg::LOD* lod = new osg::LOD; lod->addChild(tile,cut_off_distance,max_visible_distance); lod->addChild(group,0.0f,cut_off_distance); return lod; } } template< typename T> void populate_z(osg::Image* image, const osg::Vec3& zAxis,osg::Vec3Array& v) { unsigned char* ptr = image->data(); unsigned int rowSizeInBytes = image->getRowSizeInBytes(); unsigned int pixelSizeInBits = image->getPixelSizeInBits(); unsigned int pixelSizeInBytes = pixelSizeInBits/8; unsigned int numColumns = image->s(); unsigned int numRows = image->t(); for(unsigned int r=0,vi=0;r(image->getUserData()); unsigned int numColumns = image->s(); unsigned int numRows = image->t(); unsigned int r; unsigned int c; osg::Geode* geode = new osg::Geode; bool useGeometry = true; if (useGeometry) { osg::Geometry* geometry = new osg::Geometry; osg::Vec3Array& v = *(new osg::Vec3Array(numColumns*numRows)); //osg::Vec3Array& n = *(new osg::Vec3Array(numColumns*numRows)); osg::Vec2Array& t = *(new osg::Vec2Array(numColumns*numRows)); osg::UByte4Array& color = *(new osg::UByte4Array(1)); color[0].set(255,255,255,255); #if 0 osg::Vec2 tex_orig(0.0f,0.0f); float rowTexDelta = 1.0f/(float)(numRows-1); float columnTexDelta = 1.0f/(float)(numColumns-1); #else osg::Vec2 tex_orig(origin.x(),origin.y()); float columnTexDelta = xAxis.length()/(float)(numColumns-1); float rowTexDelta = yAxis.length()/(float)(numRows-1); #endif if (texCoordRange) { // tex_orig.set(texCoordRange->_x,texCoordRange->_y); // rowTexDelta = texCoordRange->_h/(float)(numRows-1); // columnTexDelta = texCoordRange->_w/(float)(numColumns-1); std::cout<<"setting tex values to use texCoordRange"<getDataType()) { case(GL_UNSIGNED_BYTE): populate_z(image,zAxis,v); break; case(GL_BYTE): populate_z(image,zAxis,v); break; case(GL_UNSIGNED_SHORT): populate_z(image,zAxis,v); break; case(GL_SHORT): populate_z(image,zAxis,v); break; case(GL_UNSIGNED_INT): populate_z(image,zAxis,v); break; case(GL_INT): populate_z(image,zAxis,v); break; case(GL_FLOAT): populate_z(image,zAxis,v); break; } geometry->setVertexArray(&v); //geometry->setNormalArray(&n); //geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX); geometry->setColorArray(&color); geometry->setColorBinding(osg::Geometry::BIND_OVERALL); geometry->setTexCoordArray(0,&t); geometry->setTexCoordArray(1,&t); for(r=0;raddPrimitiveSet(&drawElements); int ei=0; for(c=0;csetVertexAttribData(6, osg::Geometry::ArrayData(&tsgTangentArray, osg::Geometry::BIND_OVERALL,GL_FALSE)); geometry->setVertexAttribData(7, osg::Geometry::ArrayData(&tsgBinormalArray, osg::Geometry::BIND_OVERALL,GL_FALSE)); geometry->setVertexAttribData(15, osg::Geometry::ArrayData(&tsgNormalArray, osg::Geometry::BIND_OVERALL,GL_FALSE)); //geometry->setUseVertexBufferObjects(true); geometry->setUseDisplayList(false); geometry->setUseVertexBufferObjects(false); geode->addDrawable(geometry); } else { osg::HeightField* grid = createHeightField(image,origin,osg::Vec3(xAxis.length(),yAxis.length(),zAxis.length())); geode->addDrawable(new osg::ShapeDrawable(grid)); } return geode; } osg::Node* computeGeometry(osg::Image* image, const osg::Vec3& origin, const osg::Vec3& size) { return computeGeometry(image,origin,osg::Vec3(size.x(),0.0f,0.0f),osg::Vec3(0.0f,size.y(),0.0f),osg::Vec3(0.0f,0.0f,size.z())); } osg::Node* createTile(const std::string& filename, double x, double y, double w,double h) { osg::ref_ptr options = new osgDB::ImageOptions; options->_sourceImageWindowMode = osgDB::ImageOptions::RATIO_WINDOW; options->_sourceRatioWindow.set(x,1-(y+h),w,h); options->_destinationImageWindowMode = osgDB::ImageOptions::PIXEL_WINDOW; options->_destinationPixelWindow.set(0,0,20,20); osgDB::Registry::instance()->setOptions(options.get()); osg::Image* image = osgDB::readImageFile(filename.c_str()); if (image) { return computeGeometry(image,osg::Vec3(x,y,0.0),osg::Vec3(w,h,VERTICAL_SIZE)); } else { return 0; } } osg::Node* createTileAndRecurse(const std::string& filename, const std::string& basename, const std::string& extension, unsigned int noTilesX, unsigned int noTilesY, double x, double y, double w,double h, unsigned int numLevelsLeft) { osg::Group* group = new osg::Group; double dx = w / (double) noTilesX; double dy = h / (double) noTilesY; if (numLevelsLeft>0) { float cut_off_distance = 4.0f*dy; float max_visible_distance = 1e7; // create current layer, and write to disk. unsigned int numTiles = 0; double lx = x; for(unsigned i=0;i node = createTileAndRecurse(filename,lbasename,extension,2,2,lx,ly,dx,dy,numLevelsLeft-1); osgDB::writeNodeFile(*node, lbasename+extension); } // create PagedLOD for tile. osg::PagedLOD* pagedlod = new osg::PagedLOD; osg::Node* tile = createTile(filename,lx,ly,dx,dy); pagedlod->addChild(tile, cut_off_distance,max_visible_distance); pagedlod->setRange(1,0.0f,cut_off_distance); pagedlod->setFileName(1,lbasename+extension); pagedlod->setCenter(tile->getBound().center()); group->addChild(pagedlod); // increment number of tiles. ++numTiles; } } } else { double lx = x; for(unsigned i=0;iaddChild(createTile(filename,lx,ly,dx,dy)); } } } return group; } bool createWorld(const std::string& inputFile, const std::string& baseName, const std::string& diffuseTextureName,unsigned int numLevels) { osgDB::ReaderWriter* readerWriter = osgDB::Registry::instance()->getReaderWriterForExtension("gdal"); if (!readerWriter) { std::cout<<"Error: GDAL plugin not available, cannot preceed with database creation"< options = new osgDB::ImageOptions; options->_sourceImageWindowMode = osgDB::ImageOptions::RATIO_WINDOW; options->_sourceRatioWindow.set(0,0,1,1); options->_destinationImageWindowMode = osgDB::ImageOptions::PIXEL_WINDOW; options->_destinationPixelWindow.set(0,0,bumpMapSizeWidth,bumpMapSizeHeight); osgDB::Registry::instance()->setOptions(options.get()); bool useImage = false; if (useImage) { osg::Image* image = osgDB::readImageFile(inputFile.c_str()); if (image) { osg::HeightField* grid = createHeightField(image,osg::Vec3(0.0f,0.0f,0.0f),osg::Vec3(1.0,1.0,VERTICAL_SIZE)); normalMap = createNormalMap(grid); normalMap->setFileName("normalmap.rgb"); osgDB::writeImageFile(*normalMap,"normalmap.rgb"); // scene = createQuadTree(grid, // 0, grid->getNumColumns()-1, // 0, grid->getNumRows()-1, // 0.0f, 1.0f, // 0.0f, 1.0f, // 2000, numLevels); } } else { osg::HeightField* grid = osgDB::readHeightFieldFile(inputFile.c_str()); if (grid) { osg::HeightField::HeightList& hlist = grid->getHeightList(); for(osg::HeightField::HeightList::iterator itr=hlist.begin(); itr!=hlist.end(); ++itr) { (*itr) *= VERTICAL_SIZE; } normalMap = createNormalMap(grid); normalMap->setFileName("normalmap.rgb"); osgDB::writeImageFile(*normalMap,"normalmap.rgb"); // osg::Geode* geode = new osg::Geode; // geode->addDrawable(new osg::ShapeDrawable(grid)); // // scene = geode; scene = createQuadTree(grid, 0, grid->getNumColumns()-1, 0, grid->getNumRows()-1, 0.0f, 1.0f, 0.0f, 1.0f, 2000, numLevels); } } } // generate diffuse map osg::Image* diffuseMap = 0; // { // osg::ref_ptr options = new osgDB::ImageOptions; // options->_sourceImageWindowMode = osgDB::ImageOptions::RATIO_WINDOW; // options->_sourceRatioWindow.set(0,0,1,1); // // options->_destinationImageWindowMode = osgDB::ImageOptions::PIXEL_WINDOW; // options->_destinationPixelWindow.set(0,0,2048,2048); // // osgDB::Registry::instance()->setOptions(options.get()); // diffuseMap = osgDB::readImageFile(inputFile.c_str()); // if (diffuseMap) // { // diffuseMap->setFileName("diffuse.rgb"); // osgDB::writeImageFile(*diffuseMap,"diffuse.rgb"); // } // // } //diffuseMap = osgDB::readImageFile(diffuseTextureName.c_str()); diffuseMap = new osg::Image; diffuseMap->setFileName(diffuseTextureName.c_str()); osg::Texture2D* diffuseMapTexture = new osg::Texture2D(diffuseMap); osg::Texture2D* normalMapTexture = new osg::Texture2D(normalMap); // create osgFX::BumpingMapping osg::ref_ptr bumpMap = new osgFX::BumpMapping; bumpMap->setLightNumber(0); bumpMap->setNormalMapTextureUnit(0); bumpMap->setDiffuseTextureUnit(1); bumpMap->selectTechnique(1); bumpMap->setOverrideDiffuseTexture(diffuseMapTexture); bumpMap->setOverrideNormalMapTexture(normalMapTexture); bumpMap->addChild(scene.get()); #ifdef USE_PREPARE bumpMap->prepareChildren(); #endif osg::ref_ptr group = new osg::Group; group->addChild(bumpMap.get()); osg::StateSet* stateset = group->getOrCreateStateSet(); stateset->setMode(GL_CULL_FACE,osg::StateAttribute::ON); osgDB::writeNodeFile(*group, baseName); } else { osg::ref_ptr group = new osg::Group; group->addChild(createTileAndRecurse(inputFile, base, extension, 2,2, 0.0, 0.0, 1.0, 1.0, numLevels)); osg::StateSet* stateset = group->getOrCreateStateSet(); stateset->setMode(GL_CULL_FACE,osg::StateAttribute::ON); osgDB::writeNodeFile(*group, baseName); } osg::Timer_t end_tick = timer.tick(); std::cout << "Time to create world "<setApplicationName(arguments.getApplicationName()); arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the standard OpenSceneGraph example which loads and visualises 3d models."); arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ..."); arguments.getApplicationUsage()->addCommandLineOption("-i ","Specify the input file to process"); arguments.getApplicationUsage()->addCommandLineOption("-o ","Specify the output master file to generate"); arguments.getApplicationUsage()->addCommandLineOption("-l ","Specify the number of PagedLOD levels to generate"); arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information"); std::string inputFile; while (arguments.read("-i",inputFile)) {} std::string basename("output.ive"); while (arguments.read("-o",basename)) {} float numLevels; while (arguments.read("-l",numLevels)) {} while (arguments.read("-v",VERTICAL_SIZE)) {} std::string diffuseTextureName("lz.ive"); while (arguments.read("-t",diffuseTextureName)) {} // if user request help write it out to cout. if (arguments.read("-h") || arguments.read("--help")) { arguments.getApplicationUsage()->write(std::cout); return 1; } // any option left unread are converted into errors to write out later. arguments.reportRemainingOptionsAsUnrecognized(); // report any errors if they have occured when parsing the program aguments. if (arguments.errors()) { arguments.writeErrorMessages(std::cout); return 1; } // if (arguments.argc()<=1) // { // arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION); // return 1; // } // create a graphics context to allow us to use OpenGL to compress textures. GraphicsContext gfx; createWorld(inputFile,basename,diffuseTextureName,(unsigned int)numLevels); return 0; }