int test_spherical_sampling_serialization() { #ifdef MINSG_EXT_SVS std::cout << "Test serialization of SVS objects ... "; Util::Timer timer; timer.reset(); using namespace MinSG::SVS; MinSG::SceneManagement::SceneManager sceneManager; const uint32_t count = 10; std::array<Util::Reference<MinSG::GeometryNode>, count> nodes; for(uint_fast32_t i = 0; i < count; ++i) { nodes[i] = new MinSG::GeometryNode; sceneManager.registerNode(std::string("Node") + Util::StringUtils::toString(i), nodes[i].get()); } MinSG::VisibilitySubdivision::VisibilityVector vv; for(uint_fast32_t i = 0; i < count; ++i) { vv.setNode(nodes[i].get(), i); } std::vector<SamplePoint> samples; { using namespace Rendering::MeshUtils::PlatonicSolids; Util::Reference<Rendering::Mesh> mesh = createEdgeSubdivisionSphere(createIcosahedron(), 4); auto accessor = Rendering::PositionAttributeAccessor::create(mesh->openVertexData(), Rendering::VertexAttributeIds::POSITION); for(std::size_t i = 0; accessor->checkRange(i); ++i) { samples.emplace_back(accessor->getPosition(i)); } } { std::stringstream stream; stream.precision(std::numeric_limits<long double>::digits10); // Serialize for(const auto & sample : samples) { stream << sample.getPosition() << ' '; sample.getValue().serialize(stream, sceneManager); stream << ' '; } // Unserialize std::vector<SamplePoint> newSamples; for(std::size_t s = 0; s < samples.size(); ++s) { Geometry::Vec3f pos; stream >> pos; SamplePoint sample(pos); sample.setValue(MinSG::VisibilitySubdivision::VisibilityVector::unserialize(stream, sceneManager)); newSamples.push_back(sample); } for(std::size_t s = 0; s < samples.size(); ++s) { const SamplePoint & oldSample = samples[s]; const SamplePoint & newSample = newSamples[s]; if(oldSample.getPosition().distance(newSample.getPosition()) > std::numeric_limits<float>::epsilon()) { std::cout << "Serialization/unserialization failed." << std::endl; return EXIT_FAILURE; } const auto & oldVV = oldSample.getValue(); const auto & newVV = newSample.getValue(); for(uint_fast32_t n = 0; n < count; ++n) { if(oldVV.getBenefits(nodes[n].get()) != newVV.getBenefits(nodes[n].get())) { std::cout << "Serialization/unserialization failed." << std::endl; return EXIT_FAILURE; } } } } VisibilitySphere visibilitySphere(Geometry::Sphere_f(Geometry::Vec3f(1.0f, 2.0f, 3.0f), 17.0f), samples); { std::stringstream stream; stream.precision(std::numeric_limits<long double>::digits10); // Serialize stream << visibilitySphere.getSphere() << ' ' << samples.size(); for(const auto & sample : samples) { stream << ' ' << sample.getPosition() << ' '; sample.getValue().serialize(stream, sceneManager); stream << ' '; } // Unserialize Geometry::Sphere_f sphere; stream >> sphere; std::size_t sampleCount; stream >> sampleCount; std::vector<SamplePoint> newSamples; for(std::size_t s = 0; s < sampleCount; ++s) { Geometry::Vec3f pos; stream >> pos; SamplePoint sample(pos); sample.setValue(MinSG::VisibilitySubdivision::VisibilityVector::unserialize(stream, sceneManager)); newSamples.push_back(sample); } VisibilitySphere newVisibilitySphere(sphere, newSamples); if(!(visibilitySphere.getSphere() == newVisibilitySphere.getSphere())) { std::cout << "Serialization/unserialization failed." << std::endl; return EXIT_FAILURE; } for(std::size_t s = 0; s < samples.size(); ++s) { const SamplePoint & oldSample = visibilitySphere.getSamples()[s]; const SamplePoint & newSample = newVisibilitySphere.getSamples()[s]; if(oldSample.getPosition().distance(newSample.getPosition()) > std::numeric_limits<float>::epsilon()) { std::cout << "Serialization/unserialization failed." << std::endl; return EXIT_FAILURE; } const auto & oldVV = oldSample.getValue(); const auto & newVV = newSample.getValue(); for(uint_fast32_t n = 0; n < count; ++n) { if(oldVV.getBenefits(nodes[n].get()) != newVV.getBenefits(nodes[n].get())) { std::cout << "Serialization/unserialization failed." << std::endl; return EXIT_FAILURE; } } } } timer.stop(); std::cout << "done (duration: " << timer.getSeconds() << " s).\n"; #endif /* MINSG_EXT_SVS */ return EXIT_SUCCESS; }
int test_large_scene(Util::UI::Window * window, Util::UI::EventContext & eventContext) { // texture registry std::map<std::string, Util::Reference<Rendering::Texture> > textures; std::cout << "Create FrameContext...\n"; FrameContext fc; unsigned int renderingFlags = /*BOUNDING_BOXES|SHOW_META_OBJECTS|*/FRUSTUM_CULLING/*|SHOW_COORD_SYSTEM*/;//|SHOW_COORD_SYSTEM; std::cout << "Create scene graph...\n"; Util::Reference<GroupNode> root = new MinSG::ListNode(); /// Skybox SkyboxState * sb = SkyboxState::createSkybox("Data/texture/?.bmp"); root->addState(sb); /// Some shperes... { std::default_random_engine engine; std::uniform_real_distribution<float> coordinateDist(0.0f, 200.0f); std::vector<Util::Reference<Rendering::Mesh> > spheres; Util::Reference<Rendering::Mesh> icosahedron = Rendering::MeshUtils::PlatonicSolids::createIcosahedron(); for(int i=0;i<6;++i) spheres.push_back(Rendering::MeshUtils::PlatonicSolids::createEdgeSubdivisionSphere(icosahedron.get(), i)); // 6... 81920 triangles each for (int i = 0; i < 1000; i++) { // create a real clone inclusive internal data! MinSG::GeometryNode * gn = new GeometryNode(spheres[std::uniform_int_distribution<std::size_t>(0, spheres.size() - 1)(engine)]->clone()); gn->moveRel(Geometry::Vec3(coordinateDist(engine), coordinateDist(engine), coordinateDist(engine))); root->addChild(gn); gn->scale(0.1 + std::uniform_real_distribution<float>(0.0f, 1000.0f)(engine) / 400.0); } } /// Camera Node * schwein = loadModel(Util::FileName("Data/model/Schwein.low.t.ply"), MESH_AUTO_CENTER | MESH_AUTO_SCALE); ListNode * camera = new ListNode(); CameraNode * camNode = new CameraNode(); camNode->setViewport(Geometry::Rect_i(0, 0, 1024, 768)); camNode->setNearFar(0.1, 2000); camNode->applyVerticalAngle(80); camNode->moveRel(Geometry::Vec3(0, 4, 10)); camera->addChild(camNode); camera->addChild(schwein); schwein->moveRel(Geometry::Vec3(0, 0, 0)); schwein->rotateLocal_deg(180, Geometry::Vec3(0, 1, 0)); LightNode * myHeadLight = LightNode::createPointLight(); myHeadLight->scale(1); myHeadLight->moveRel(Geometry::Vec3(0, 0, 0)); camera->addChild(myHeadLight); LightingState * lightState = new LightingState; lightState->setLight(myHeadLight); root->addState(lightState); root->addChild(camera); /// Eventhandler MoveNodeHandler * eh = new MoveNodeHandler(); MoveNodeHandler::initClaudius(eh, camera); // --------------------------------------------------------------------------------------------- Rendering::RenderingContext::clearScreen(Util::Color4f(0.5f, 0.5f, 0.5f, 0.5f)); // ---- GET_GL_ERROR(); uint32_t fpsFrameCounter = 0; Util::Timer fpsTimer; std::cout << "\nEntering main loop...\n"; // program main loop bool done = false; while (!done) { ++fpsFrameCounter; double seconds = fpsTimer.getSeconds(); if (seconds > 1.0) { double fps = static_cast<double> (fpsFrameCounter) / seconds; std::cout << "\r " << fps << " fps "; std::cout.flush(); fpsTimer.reset(); fpsFrameCounter = 0; } // message processing loop eventContext.getEventQueue().process(); while (eventContext.getEventQueue().getNumEventsAvailable() > 0) { Util::UI::Event event = eventContext.getEventQueue().popEvent(); // check for messages switch (event.type) { // exit if the window is closed case Util::UI::EVENT_QUIT: done = true; break; // check for keypresses case Util::UI::EVENT_KEYBOARD: { if(event.keyboard.pressed && event.keyboard.key == Util::UI::KEY_ESCAPE) { done = true; } break; } } // end switch } // end of message processing // apply translation eh->execute(); // clear screen Rendering::RenderingContext::clearScreen(Util::Color4f(0.0f, 0.0f, 0.0f, 1.0f)); // enable Camera fc.setCamera(camNode); // render Scene root->display(fc, renderingFlags); window->swapBuffers(); GET_GL_ERROR(); } // end main loop // destroy scene graph MinSG::destroy(root.get()); root = nullptr; // all is well ;) std::cout << "Exited cleanly\n"; //system("pause"); return EXIT_SUCCESS; }
int test_spherical_sampling() { #ifdef MINSG_EXT_SVS std::cout << "Test SVS ... "; Util::Timer timer; timer.reset(); MinSG::SceneManagement::SceneManager sceneManager; MinSG::FrameContext frameContext; Rendering::VertexDescription vertexDesc; vertexDesc.appendPosition3D(); Util::Reference<Rendering::Mesh> boxMesh = Rendering::MeshUtils::MeshBuilder::createBox(vertexDesc, Geometry::Box(Geometry::Vec3f(0.5f, 0.5f, 0.5f), 1)); /* * Create a 1D array of n unit-size boxes in the x axis * * / * ..L2 * / / * ......L1 / * / / / * ...L0.... / / * / / / / / * ------------------------- ... ----- * | 0 | 1 | 2 | 3 | 4 | 5 | | n | * | | | | | | | | | * ------------------------- ... ----- * ^ ^ ^ * | | | * x=0 x=4 x=n * * The first three boxes are put into the first list node L0. * L0 together with the fourth box are put into the second list node L1. */ const uint32_t count = 512; std::vector<Util::Reference<MinSG::ListNode>> listNodes; std::vector<Util::Reference<MinSG::GeometryNode>> boxNodes; for(uint_fast32_t x = 0; x < count; ++x) { MinSG::GeometryNode * geoNode = new MinSG::GeometryNode(boxMesh); geoNode->moveLocal(Geometry::Vec3f(x, 0, 0)); sceneManager.registerNode(std::string("Node") + Util::StringUtils::toString(x), geoNode); boxNodes.push_back(geoNode); if(x != 1 && x != 2) { listNodes.push_back(new MinSG::ListNode); } if(x < 3) { listNodes.front()->addChild(geoNode); } else { listNodes[x - 2]->addChild(listNodes[x - 3].get()); listNodes[x - 2]->addChild(geoNode); } } // Debug output of created scene graph //MinSG::GraphVizOutput::treeToFile(listNodes.back().get(), &sceneManager, Util::FileName("output.dot")); // Perform the sampling std::vector<Geometry::Vec3f> positions; positions.emplace_back(-1, 0, 0); // left positions.emplace_back(1, 0, 0); // right positions.emplace_back(0, 1, 0); // top positions.emplace_back(0, 0, 1); // back // The resolution has to be at least the number of boxes. Otherwise, boxes will be missed during visibility testing. MinSG::SVS::PreprocessingContext preprocessingContext(sceneManager, frameContext, listNodes.back().get(), positions, count, false, false); while(!preprocessingContext.isFinished()) { preprocessingContext.preprocessSingleNode(); } // Validate the results for(uint_fast32_t listIndex = 0; listIndex < count - 2; ++listIndex) { MinSG::ListNode * listNode = listNodes[listIndex].get(); const MinSG::SVS::VisibilitySphere & visibilitySphere = MinSG::SVS::retrieveVisibilitySphere(listNode); { // Because the geometry is built of axis-aligned bounding boxes only, the sphere has to contain the group's bounding box. assert(visibilitySphere.getSphere().distance(listNode->getBB().getMin()) < 1.0e-9); assert(visibilitySphere.getSphere().distance(listNode->getBB().getMax()) < 1.0e-9); } { // left => only first box must be visible const auto vv = visibilitySphere.queryValue(positions[0], MinSG::SVS::INTERPOLATION_NEAREST); assert(vv.getBenefits(boxNodes.front().get()) > 0); for(uint_fast32_t boxIndex = 1; boxIndex < count; ++boxIndex) { MinSG::GeometryNode * geoNode = boxNodes[boxIndex].get(); assert(vv.getBenefits(geoNode) == 0); } } { // right => only last box inside the subtree must be visible const auto vv = visibilitySphere.queryValue(positions[1], MinSG::SVS::INTERPOLATION_NEAREST); const uint32_t lastBoxIndex = listIndex + 2; assert(vv.getBenefits(boxNodes[lastBoxIndex].get()) > 0); for(uint_fast32_t boxIndex = 0; boxIndex < count; ++boxIndex) { if(boxIndex != lastBoxIndex) { MinSG::GeometryNode * geoNode = boxNodes[boxIndex].get(); assert(vv.getBenefits(geoNode) == 0); } } } // top, back => all boxes in the subtree must be visible for(uint_fast32_t posIndex = 2; posIndex <= 3; ++posIndex) { const auto vv = visibilitySphere.queryValue(positions[posIndex], MinSG::SVS::INTERPOLATION_NEAREST); const auto geoNodes = MinSG::collectNodes<MinSG::GeometryNode>(listNode); for(const auto & geoNode : geoNodes) { assert(vv.getBenefits(geoNode) > 0); } } } boxNodes.clear(); MinSG::destroy(listNodes.back().get()); listNodes.clear(); timer.stop(); std::cout << "done (duration: " << timer.getSeconds() << " s).\n"; #endif /* MINSG_EXT_SVS */ return EXIT_SUCCESS; }
int test_OutOfCore() { #ifdef MINSG_EXT_OUTOFCORE const bool verbose = true; // Tests for MinSG::OutOfCore::CacheObjectPriority if(sizeof(MinSG::OutOfCore::CacheObjectPriority) != 8) { return EXIT_FAILURE; } if(!(MinSG::OutOfCore::CacheObjectPriority(1, 2, 3) == MinSG::OutOfCore::CacheObjectPriority(1, 2, 3))) { return EXIT_FAILURE; } if(!(MinSG::OutOfCore::CacheObjectPriority(1, 100, 100) < MinSG::OutOfCore::CacheObjectPriority(2, 0, 0))) { return EXIT_FAILURE; } if(!(MinSG::OutOfCore::CacheObjectPriority(2, 1, 100) < MinSG::OutOfCore::CacheObjectPriority(2, 2, 0))) { return EXIT_FAILURE; } if(!(MinSG::OutOfCore::CacheObjectPriority(2, 2, 1) < MinSG::OutOfCore::CacheObjectPriority(2, 2, 2))) { return EXIT_FAILURE; } std::default_random_engine engine; std::uniform_int_distribution<std::size_t> vertexCountDist(10, 1000); const uint32_t numMeshes = 30000; const Util::TemporaryDirectory tempDir("MinSGTest_OutOfCore"); // Create empty meshes and save them into a subdirectory. { Rendering::VertexDescription vertexDesc; vertexDesc.appendPosition3D(); for(uint_fast32_t i = 0; i < numMeshes; ++i) { Util::Reference<Rendering::Mesh> mesh = new Rendering::Mesh(vertexDesc, vertexCountDist(engine), 64); Rendering::MeshVertexData & vertexData = mesh->openVertexData(); std::fill_n(vertexData.data(), vertexData.dataSize(), 0); vertexData.markAsChanged(); Rendering::MeshIndexData & indexData = mesh->openIndexData(); std::fill_n(indexData.data(), indexData.getIndexCount(), 0); indexData.markAsChanged(); const std::string numberString = Util::StringUtils::toString<uint32_t>(i); Rendering::Serialization::saveMesh(mesh.get(), Util::FileName(tempDir.getPath().getDir() + numberString + ".mmf")); } } // Set up the OutOfCore system. MinSG::FrameContext frameContext; MinSG::OutOfCore::setUp(frameContext); MinSG::OutOfCore::CacheManager & manager = MinSG::OutOfCore::getCacheManager(); manager.addCacheLevel(MinSG::OutOfCore::CacheLevelType::FILE_SYSTEM, 0); manager.addCacheLevel(MinSG::OutOfCore::CacheLevelType::FILES, 512 * kibibyte); manager.addCacheLevel(MinSG::OutOfCore::CacheLevelType::MAIN_MEMORY, 256 * kibibyte); Util::Timer addTimer; addTimer.reset(); std::cout << "Adding meshes ..." << std::flush; // Add the meshes to the OutOfCore system. std::vector<Util::Reference<Rendering::Mesh> > meshes; meshes.reserve(numMeshes); static const Geometry::Box boundingBox(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f); for(uint_fast32_t i = 0; i < numMeshes; ++i) { const std::string numberString = Util::StringUtils::toString<uint32_t>(i); meshes.push_back(MinSG::OutOfCore::addMesh(Util::FileName(tempDir.getPath().getDir() + numberString + ".mmf"), boundingBox)); } manager.trigger(); addTimer.stop(); std::cout << " done (" << addTimer.getSeconds() << " s)" << std::endl; Util::Timer displayTimer; Util::Timer assureLocalTimer; Util::Timer overallTimer; overallTimer.reset(); uint32_t frame = 0; { // Simulate frames to get the OutOfCore system working. std::uniform_int_distribution<std::size_t> indexDist(0, meshes.size() - 1); for(; frame < 10; ++frame) { std::cout << "Executing frame " << frame << " ..." << std::flush; frameContext.beginFrame(); displayTimer.reset(); // Simulate display of meshes to change the priorities of the system. for(uint32_t i = 0; i < meshes.size() / 2; ++i) { const uint32_t meshIndex = indexDist(engine); Rendering::Mesh * mesh = meshes[meshIndex].get(); manager.meshDisplay(mesh); } manager.trigger(); displayTimer.stop(); assureLocalTimer.reset(); for(uint32_t i = 0; i < 10; ++i) { const uint32_t meshIndex = indexDist(engine); Rendering::Mesh * mesh = meshes[meshIndex].get(); const Rendering::MeshVertexData & vd = mesh->openVertexData(); const Rendering::MeshIndexData & id = mesh->openIndexData(); if (!vd.hasLocalData() || !id.hasLocalData()) { std::cout << "Error: Mesh has no local data." << std::endl; return EXIT_FAILURE; } } assureLocalTimer.stop(); frameContext.endFrame(); std::cout << " done (display: " << displayTimer.getSeconds() << " s, assureLocal: " << assureLocalTimer.getSeconds() << " s)" << std::endl; if(verbose) { outputCacheLevelInformation(); } } } for(uint32_t round = 0; round < 10; ++round) { Util::Timer addAgainTimer; addAgainTimer.reset(); std::cout << "Adding additional meshes ..." << std::flush; // Simulate loading a second scene by adding meshes again. meshes.reserve(meshes.size() + 3 * numMeshes); for(uint_fast32_t i = 0; i < numMeshes; ++i) { const std::string numberString = Util::StringUtils::toString<uint32_t>(i); meshes.push_back(MinSG::OutOfCore::addMesh(Util::FileName(tempDir.getPath().getDir() + numberString + ".mmf"), boundingBox)); meshes.push_back(MinSG::OutOfCore::addMesh(Util::FileName(tempDir.getPath().getDir() + numberString + ".mmf"), boundingBox)); meshes.push_back(MinSG::OutOfCore::addMesh(Util::FileName(tempDir.getPath().getDir() + numberString + ".mmf"), boundingBox)); } manager.trigger(); addAgainTimer.stop(); std::cout << " done (" << addAgainTimer.getSeconds() << " s)" << std::endl; // Simulate frames to get the OutOfCore system working. std::normal_distribution<double> indexDist(meshes.size() / 2, std::sqrt(meshes.size() / 2)); const auto untilFrame = frame + 5; for(; frame < untilFrame; ++frame) { std::cout << "Executing frame " << frame << " ..." << std::flush; frameContext.beginFrame(); displayTimer.reset(); // Simulate display of meshes to change the priorities of the system. for(uint32_t i = 0; i < meshes.size() / 10; ++i) { const std::size_t meshIndex = std::max(static_cast<std::size_t>(0), std::min(static_cast<std::size_t>(indexDist(engine)), meshes.size() - 1)); Rendering::Mesh * mesh = meshes[meshIndex].get(); manager.meshDisplay(mesh); } manager.trigger(); displayTimer.stop(); frameContext.endFrame(); std::cout << " done (display: " << displayTimer.getSeconds() << " s)" << std::endl; if(verbose) { outputCacheLevelInformation(); } } } overallTimer.stop(); std::cout << "Overall duration: " << overallTimer.getSeconds() << " s" << std::endl; MinSG::OutOfCore::shutDown(); return EXIT_SUCCESS; #else /* MINSG_EXT_OUTOFCORE */ return EXIT_FAILURE; #endif /* MINSG_EXT_OUTOFCORE */ }