State::stateResult_t OccludeeRenderer::doEnableState(FrameContext & context, Node * rootNode, const RenderParam & rp) { if (rp.getFlag(SKIP_RENDERER)) { return State::STATE_SKIPPED; } static PolygonModeState polygonMode; polygonMode.changeParameters().setMode(Rendering::PolygonModeParameters::LINE); occlusionCullingRenderer->setMode(OccRenderer::MODE_CULLING); const State::stateResult_t resultCulling = occlusionCullingRenderer->enableState(context, rootNode, rp); if(resultCulling != State::STATE_SKIP_RENDERING) { WARN("Using the OccRenderer for culling failed."); return resultCulling; } // Clear the depth buffer to be able to render the occluded geometry in front of the previously rendered geometry. context.getRenderingContext().clearDepth(1.0f); // Activate wireframe mode. polygonMode.enableState(context, rootNode, rp); occlusionCullingRenderer->setMode(OccRenderer::MODE_SHOW_CULLED); RenderParam rpWorldMatrix = rp; rpWorldMatrix.setFlag(USE_WORLD_MATRIX); const State::stateResult_t resultShowCulled = occlusionCullingRenderer->enableState(context, rootNode, rpWorldMatrix); if(resultShowCulled != State::STATE_SKIP_RENDERING) { WARN("Using the OccRenderer for showing the culled geometry failed."); return resultShowCulled; } // Deactivate wireframe mode. polygonMode.disableState(context, rootNode, rp); return State::STATE_SKIP_RENDERING; }
void FakeInstanceNode::doDisplay(FrameContext & frameContext, const RenderParam & rp) { if(fakePrototype.isNull()) { Node::doDisplay(frameContext, rp); return; } auto camera = static_cast<AbstractCameraNode*>(frameContext.getCamera()->clone()); const Geometry::Matrix4x4f & cameraMatrix = frameContext.getCamera()->getWorldTransformationMatrix(); camera->setRelTransformation( fakePrototype->getWorldTransformationMatrix() * getWorldToLocalMatrix() * cameraMatrix); frameContext.pushAndSetCamera(camera); frameContext.displayNode(fakePrototype.get(), rp); frameContext.popCamera(); }
void OccRenderer::updateNodeInformation(FrameContext & context,Node * rootNode)const{ /*** ** Vis ---|> NodeVisitor ***/ struct Vis:public NodeVisitor { const OccRenderer & r; AbstractCameraNode * camera; const Node * rootNode; int insideFrustum; Geometry::Vec3 camPos; Vis(const OccRenderer & _r, const Node * _rootNode, AbstractCameraNode * _camera) : r(_r), camera(_camera), rootNode(_rootNode), insideFrustum(0), camPos(_camera->getWorldOrigin()) {} virtual ~Vis() {} // ---|> NodeVisitor NodeVisitor::status enter(Node * node) override { NodeInfo * info=r.getNodeInfo(node); info->setActualSubtreeComplexity(0); // the current subtree is completely inside the frustum, so we need not to test inside this subtree if(insideFrustum>0){ info->setActualFrustumStatus(Geometry::Frustum::INSIDE); insideFrustum++; }else{ int i=camera->testBoxFrustumIntersection( node->getWorldBB()); info->setActualFrustumStatus(i); if(i==Geometry::Frustum::OUTSIDE){ return NodeVisitor::BREAK_TRAVERSAL; }else if(i==Geometry::Frustum::INSIDE){ insideFrustum++; } } if(node->isClosed() && node!=rootNode){ unsigned int c = insideFrustum > 0 ? countTriangles(node) : countTrianglesInFrustum(node, camera->getFrustum()); info->increaseActualSubtreeComplexity( c ); return BREAK_TRAVERSAL; } return CONTINUE_TRAVERSAL; } // ---|> NodeVisitor NodeVisitor::status leave(Node * node) override { if(insideFrustum>0){ insideFrustum--; } unsigned int complexity=r.getNodeInfo(node)->getActualSubtreeComplexity(); if(complexity>0 && rootNode!=node && node->hasParent()){ NodeInfo * pinfo=r.getNodeInfo(node->getParent()); pinfo->increaseActualSubtreeComplexity(complexity); } return CONTINUE_TRAVERSAL; } } visitor(*this,rootNode,context.getCamera()); rootNode->traverse(visitor); return; }
//! (internal) bool ColorCubeGenerator::prepareCamera(FrameContext& context, const Box& _box, Geometry::side_t side) { Geometry::Box box(_box); camera->resetRelTransformation(); camera->setRelOrigin(box.getCenter()); switch(side){ case Geometry::SIDE_X_POS: camera->rotateRel(Geometry::Angle::deg(90.0f), Geometry::Vec3(0, 1, 0)); if(box.getExtentY() == 0 || box.getExtentZ() == 0) return false; // width or height equal zero if(box.getExtentX() == 0) box.resizeAbs(0.1,0,0); // depth equal zero box.resizeRel(1.01,1,1); break; case Geometry::SIDE_X_NEG: camera->rotateRel(Geometry::Angle::deg(-90.0f), Geometry::Vec3(0, 1, 0)); if(box.getExtentY() == 0 || box.getExtentZ() == 0) return false; // width or height equal zero if(box.getExtentX() == 0) box.resizeAbs(0.1,0,0); // depth equal zero box.resizeRel(1.01,1,1); break; case Geometry::SIDE_Y_POS: camera->rotateRel(Geometry::Angle::deg(-90.0f), Geometry::Vec3(1, 0, 0)); if(box.getExtentX() == 0 || box.getExtentZ() == 0) return false; // width or height equal zero if(box.getExtentY() == 0) box.resizeAbs(0,0.1,0); // depth equal zero box.resizeRel(1,1.01,1); break; case Geometry::SIDE_Y_NEG: camera->rotateRel(Geometry::Angle::deg(90.0f), Geometry::Vec3(1, 0, 0)); if(box.getExtentX() == 0 || box.getExtentZ() == 0) return false; // width or height equal zero if(box.getExtentY() == 0) box.resizeAbs(0,0.1,0); // depth equal zero box.resizeRel(1,1.01,1); break; case Geometry::SIDE_Z_POS: if(box.getExtentX() == 0 || box.getExtentY() == 0) return false; // width or height equal zero if(box.getExtentZ() == 0) box.resizeAbs(0,0,0.1); // depth equal zero box.resizeRel(1,1,1.01); break; case Geometry::SIDE_Z_NEG: camera->rotateRel(Geometry::Angle::deg(180.0f), Geometry::Vec3(0, 1, 0)); if(box.getExtentX() == 0 || box.getExtentY() == 0) return false; // width or height equal zero if(box.getExtentZ() == 0) box.resizeAbs(0,0,0.1); // depth equal zero box.resizeRel(1,1,1.01); break; default: throw std::logic_error("the roof is on fire"); } //box.resizeRel(1.01); Geometry::Frustum f = Geometry::calcEnclosingOrthoFrustum(box, camera->getWorldTransformationMatrix().inverse()); camera->setClippingPlanes(f.getLeft(), f.getRight(), f.getBottom(), f.getTop()); camera->setNearFar(f.getNear() , f.getFar()); camera->setViewport(Geometry::Rect_i(maxTexSize*static_cast<uint8_t>(side), 0, maxTexSize, maxTexSize), true); context.setCamera(camera.get()); return true; }
State::stateResult_t OccRenderer::showCulled(FrameContext & context,Node * rootNode, const RenderParam & rp){ std::deque<Node *> nodes; nodes.push_back(rootNode); while(!nodes.empty()){ Node * node=nodes.front(); nodes.pop_front(); NodeInfo * nInfo=getNodeInfo(node); if(!conditionalFrustumTest(context.getCamera()->getFrustum(), node->getWorldBB(), rp)) continue; else if(node->isClosed() ){ if(nInfo->getVisibleFrameNumber()!=frameNumber) context.displayNode(node,rp); }else { const auto children = getChildNodes(node); nodes.insert(nodes.end(), children.begin(), children.end()); } } return State::STATE_SKIP_RENDERING; }
//! ---|> [State] State::stateResult_t ShaderUniformState::doEnableState(FrameContext & context, Node *, const RenderParam & /*rp*/) { priorValueStack.push( std::vector<Rendering::Uniform>() ); Rendering::Shader * shader = context.getRenderingContext().getActiveShader(); if(shader!=nullptr){ // assure that possible changes in the renderingContext are applied to the sg-uniforms before querying the uniforms context.getRenderingContext().applyChanges(); for(const auto & uniformEntry : uMap) { const Rendering::Uniform & uniform(uniformEntry.second); const Rendering::Uniform & priorUniform(shader->getUniform(uniform.getNameId())); if(priorUniform.isNull()) continue; priorValueStack.top().push_back(priorUniform); shader->setUniform(context.getRenderingContext(),uniform,true); // warn if unused--> should not happen } } return State::STATE_OK; }
State::stateResult_t SurfelRenderer::doEnableState(FrameContext & context, Node * node, const RenderParam & rp) { State::stateResult_t result = NodeRendererState::doEnableState(context,node,rp); if( result == State::STATE_OK ){ CameraNode* camera = dynamic_cast<CameraNode*>( context.getCamera() ); if(camera){ // else: an orthographic camera is used; just re-use the previous values. cameraOrigin = camera->getWorldOrigin(); projectionScale = camera->getWidth() / (std::tan((camera->getRightAngle()-camera->getLeftAngle()).rad()*0.5f)*2.0f); } } return result; }
bool VisibilitySubdivisionRenderer::renderCellSubset(FrameContext & context, cell_ptr cell, uint32_t budgetBegin, uint32_t budgetEnd) { try { const auto & vv = getVV(cell); std::vector<std::pair<float, object_ptr>> displayObjects; const uint32_t maxIndex = vv.getIndexCount(); for(uint_fast32_t index = 0; index < maxIndex; ++index) { if(vv.getBenefits(index) == 0) { continue; } const VisibilityVector::costs_t costs = vv.getCosts(index); const VisibilityVector::benefits_t benefits = vv.getBenefits(index); const float score = static_cast<float>(costs) / static_cast<float>(benefits); displayObjects.emplace_back(score, vv.getNode(index)); } const Geometry::Frustum & frustum = context.getCamera()->getFrustum(); uint32_t renderedTriangles = 0; std::sort(displayObjects.begin(), displayObjects.end()); for(const auto & objectRatioPair : displayObjects) { object_ptr o = objectRatioPair.second; Geometry::Frustum::intersection_t result = frustum.isBoxInFrustum(o->getWorldBB()); if (result == Geometry::Frustum::intersection_t::INSIDE || result == Geometry::Frustum::intersection_t::INTERSECT) { if(renderedTriangles >= budgetBegin) { context.displayNode(o, 0); } renderedTriangles += o->getTriangleCount(); } if (budgetEnd != 0 && renderedTriangles >= budgetEnd) { break; } } } catch(...) { // Invalid information. => Fall back to standard rendering. return false; } return true; }
//! Distribute the budget based on the projected size of the child nodes. static void distributeProjectedSize(double value, const Util::StringIdentifier & attributeId, Node * node, FrameContext & context) { const auto children = getChildNodes(node); // A pair stores a node and its projected size. std::vector<std::pair<Node *, float>> nodeProjSizePairs; nodeProjSizePairs.reserve(children.size()); double projSizeSum = 0.0; const Geometry::Rect_f screenRect(context.getRenderingContext().getWindowClientArea()); for(const auto & child : children) { // Clip the projected rect to the screen. auto projRect = context.getProjectedRect(child); projRect.clipBy(screenRect); const auto projSize = projRect.getArea(); projSizeSum += projSize; nodeProjSizePairs.emplace_back(child, projSize); } for(const auto & nodeProjSizePair : nodeProjSizePairs) { const double factor = nodeProjSizePair.second / projSizeSum; setOrUpdateAttribute(nodeProjSizePair.first, attributeId, factor * value); } }
void VisibilitySubdivisionRenderer::debugDisplay(uint32_t & renderedTriangles, object_ptr object, FrameContext & context, const RenderParam & rp) { const Util::Color4f specular(0.1f, 0.1f, 0.1f, 1.0f); const float ratio = static_cast<float> (renderedTriangles) / static_cast<float> (maxRuntime); // Gradient from light blue to red. Util::Color4f color(ratio, 0.5f - 0.5f * ratio, 1.0f - ratio, 1.0f); Rendering::MaterialParameters material; material.setAmbient(color); material.setDiffuse(color); material.setSpecular(specular); material.setShininess(64.0f); context.getRenderingContext().pushAndSetMaterial(material); context.displayNode(object, rp); context.getRenderingContext().popMaterial(); renderedTriangles += object->getTriangleCount(); }
//! ---|> [State] State::stateResult_t ShaderState::doEnableState(FrameContext & context, Node *, const RenderParam & /*rp*/) { if( !shader ) return State::STATE_SKIPPED; auto & rCtxt = context.getRenderingContext(); // push current shader to stack rCtxt.pushAndSetShader(shader.get()); if( !rCtxt.isShaderEnabled(shader.get()) ){ rCtxt.popShader(); deactivate(); WARN("Enabling ShaderState failed. State has been deactivated!"); return State::STATE_SKIPPED; } for(const auto & uniformEntry : uMap) shader->setUniform(rCtxt, uniformEntry.second); return State::STATE_OK; }
/** * (internal) */ void OccRenderer::processNode(FrameContext & context,Node * node,NodeInfo * nodeInfo,NodeDistancePriorityQueue_F2B & distanceQueue,const RenderParam & rp){ if (!node->isActive()) return; nodeInfo->setProcessedFrameNumber(frameNumber); if(node->isClosed()){ context.displayNode(node, rp ); }else{ const auto children = getChildNodes(node); for(auto & child : children){ NodeInfo * i=getNodeInfo(child); if( rp.getFlag(FRUSTUM_CULLING) && i->getActualFrustumStatus()==Geometry::Frustum::OUTSIDE) //context.getCamera()->testBoxFrustumIntersection( (*it)->getWorldBB()) == Frustum::OUTSIDE ) continue; if( i->getActualSubtreeComplexity() == 0){ continue; } distanceQueue.push(child); } } }
void ParticlePointRenderer::operator()(ParticleSystemNode* psys, FrameContext & context, const RenderParam & rp /* = 0 */) { if ( (rp.getFlag(NO_GEOMETRY)) ) return; // render particles std::vector<Particle> & particles = psys->getParticles(); uint32_t count = psys->getParticleCount(); Rendering::VertexDescription vertexDesc; const Rendering::VertexAttribute & posAttrib = vertexDesc.appendPosition3D(); const Rendering::VertexAttribute & colorAttrib = vertexDesc.appendColorRGBAByte(); // The usage of a cache for the mesh has been tested. Reusing a preallocated mesh is not faster. Util::Reference<Rendering::Mesh> mesh = new Rendering::Mesh(vertexDesc, count, count); mesh->setDataStrategy(Rendering::SimpleMeshDataStrategy::getPureLocalStrategy()); mesh->setDrawMode(Rendering::Mesh::DRAW_POINTS); // mesh->setUseIndexData(false); Rendering::MeshIndexData & indexData = mesh->openIndexData(); Rendering::MeshVertexData & vertexData = mesh->openVertexData(); Util::Reference<Rendering::PositionAttributeAccessor> positionAccessor = Rendering::PositionAttributeAccessor::create(vertexData, posAttrib.getNameId()); Util::Reference<Rendering::ColorAttributeAccessor> colorAccessor = Rendering::ColorAttributeAccessor::create(vertexData, colorAttrib.getNameId()); uint32_t * indices = indexData.data(); for(uint_fast32_t index = 0; index < count; ++index) { const Particle & p = particles[index]; colorAccessor->setColor(index, p.color); positionAccessor->setPosition(index, p.position); *indices++ = index; } indexData.markAsChanged(); indexData.updateIndexRange(); vertexData.markAsChanged(); vertexData.updateBoundingBox(); context.displayMesh(mesh.get()); }
//! ---|> [State] State::stateResult_t EnvironmentState::doEnableState(FrameContext & context, Node * /*node*/, const RenderParam & rp) { if (rp.getFlag(NO_GEOMETRY)) return State::STATE_SKIPPED; if (!environment.isNull()) { // extract rotation of the camera Geometry::SRT camRotationSrt = context.getRenderingContext().getMatrix_worldToCamera()._toSRT(); camRotationSrt.setTranslation(Geometry::Vec3(0,0,0)); camRotationSrt.setScale(1.0); // render the environment node as seen standing at the origin (0,0,0) but looking in the camera's direction. context.getRenderingContext().pushMatrix_modelToCamera(); context.getRenderingContext().setMatrix_modelToCamera(Geometry::Matrix4x4(camRotationSrt)); context.getRenderingContext().multMatrix_modelToCamera(Geometry::Matrix4x4f(Geometry::SRT(Geometry::Vec3f(0,0,0), context.getWorldFrontVector(), context.getWorldUpVector()))); context.displayNode(environment.get(), rp); context.getRenderingContext().popMatrix_modelToCamera(); } return State::STATE_OK; }
//! Distribute the budget based on the projected size and the primitive count of the child nodes in an iterative manner for correct distribution. static void distributeProjectedSizeAndPrimitiveCountIterative(double value, const Util::StringIdentifier & attributeId, Node * node, FrameContext & context) { static const Util::StringIdentifier primitiveCountId("PrimitiveCount"); struct PrimitiveCountAnnotationVisitor : public NodeVisitor { const Util::StringIdentifier & m_primitiveCountId; PrimitiveCountAnnotationVisitor(const Util::StringIdentifier & p_primitiveCountId) : m_primitiveCountId(p_primitiveCountId) { } virtual ~PrimitiveCountAnnotationVisitor() { } NodeVisitor::status leave(Node * _node) override { auto geoNode = dynamic_cast<GeometryNode *>(_node); uint32_t primitiveCount = 0; if(geoNode != nullptr) { const auto mesh = geoNode->getMesh(); primitiveCount = mesh == nullptr ? 0 : mesh->getPrimitiveCount(); } else { const auto children = getChildNodes(_node); for(const auto & child : children) { primitiveCount += child->getAttribute(m_primitiveCountId)->toUnsignedInt(); } } _node->setAttribute(m_primitiveCountId, Util::GenericAttribute::createNumber(primitiveCount)); return CONTINUE_TRAVERSAL; } }; const auto children = getChildNodes(node); // A pair stores the primitive count, the node and its projected size. std::deque<std::tuple<uint32_t, Node *, float>> primitiveCountNodeProjSizeTuples; double projSizeSum = 0.0; const Geometry::Rect_f screenRect(context.getRenderingContext().getWindowClientArea()); for(const auto & child : children) { if(!child->isAttributeSet(primitiveCountId)) { PrimitiveCountAnnotationVisitor visitor(primitiveCountId); child->traverse(visitor); } const auto primitiveCount = child->getAttribute(primitiveCountId)->toUnsignedInt(); // Clip the projected rect to the screen. auto projRect = context.getProjectedRect(child); projRect.clipBy(screenRect); const auto projSize = projRect.getArea(); projSizeSum += projSize; primitiveCountNodeProjSizeTuples.emplace_back(primitiveCount, child, projSize); } // Begin with the node with the lowest primitive count std::sort(primitiveCountNodeProjSizeTuples.begin(), primitiveCountNodeProjSizeTuples.end()); /* Distribute budget until one node gets all budget it asked for. * This means the previous distributions would receive more budget, * thus remove that node from distribution (update value and projSizeSum) * and start again. */ std::deque<std::tuple<uint32_t, Node *, float>> tmpDeque(primitiveCountNodeProjSizeTuples); // TODO: fails to copy values correctly!!! bool nodeRemoved; double tmpValue; double tmpProjSizeSum; double remainingProjSizeSum = projSizeSum; do{ nodeRemoved = false; tmpValue = value; tmpProjSizeSum = remainingProjSizeSum; for(auto it=tmpDeque.begin(); it!=tmpDeque.end(); ++it) { std::tuple<uint32_t, Node *, float> element = *it; const auto primitiveCount = std::get<0>(element); const auto projSize = std::get<2>(element); const auto projSizeFactor = projSize / tmpProjSizeSum; const auto primitiveAssignment = std::min(static_cast<uint32_t>(projSizeFactor * tmpValue), primitiveCount); setOrUpdateAttribute(std::get<1>(element), attributeId, primitiveAssignment); if(primitiveAssignment == primitiveCount){ nodeRemoved = true; tmpDeque.erase(it); value -= primitiveCount; remainingProjSizeSum -= projSize; break; } tmpValue -= primitiveAssignment; tmpProjSizeSum -= projSize; } }while(nodeRemoved); // distribute remaining part of value based on projected size only if(tmpValue >= 1){ for(const auto & primitiveCountNodeProjSizeTuple : primitiveCountNodeProjSizeTuples) { const auto projSize = std::get<2>(primitiveCountNodeProjSizeTuple); const auto projSizeFactor = projSize / projSizeSum; const auto attribute = dynamic_cast<Util::_NumberAttribute<double> *>(std::get<1>(primitiveCountNodeProjSizeTuple)->getAttribute(attributeId)); attribute->set(attribute->get() + projSizeFactor * tmpValue); } } }
//! ---|> Evaluator void OccOverheadEvaluator::measure(FrameContext & context,Node & node,const Geometry::Rect & /*r*/) { //float currentRenderingTime=0; //int numTests=0; //int numVBO=0; int numPoly=0; Util::Color4f clearColor(0.5f, 0.5f, 0.9f, 1.0f); { // 1. normal render pass // rc.clearScreen(0.5,0.5,0.9); // context.getRenderingContext().finish(); // node.display(rc,renderingFlags); context.getRenderingContext().clearScreen(clearColor); node.display(context,renderingFlags|USE_WORLD_MATRIX); context.getRenderingContext().clearScreen(clearColor); context.getRenderingContext().finish(); context.getStatistics().beginFrame(); node.display(context,renderingFlags|USE_WORLD_MATRIX); context.getRenderingContext().finish(); context.getStatistics().endFrame(); numPoly=static_cast<int>(context.getStatistics().getValueAsDouble(context.getStatistics().getTrianglesCounter())); } // 2. test all group nodes if their bb is visible and collect them std::deque<GroupNode *> visibleGroupNodes; { // collect geometry nodes in frustum const auto groupNodesInVFList = collectNodesInFrustum<GroupNode>(&node,context.getCamera()->getFrustum()); // setup occlusion queries int numQueries=groupNodesInVFList.size(); if (numQueries==0) { values->push_back(nullptr); // TODO!!! return; // TODO?? } auto queries = new Rendering::OcclusionQuery[numQueries]; // test bounding boxes context.getRenderingContext().pushAndSetDepthBuffer(Rendering::DepthBufferParameters(true, true, Rendering::Comparison::LEQUAL)); int i=0; // start queries Rendering::OcclusionQuery::enableTestMode(context.getRenderingContext()); for(const auto & groupNode : groupNodesInVFList) { queries[i].begin(); // queries[i].fastBoxTest((*it)->getWorldBB()); Rendering::drawAbsBox(context.getRenderingContext(), groupNode->getWorldBB() ); queries[i].end(); ++i; } Rendering::OcclusionQuery::disableTestMode(context.getRenderingContext()); i=0; // get results for(const auto & groupNode : groupNodesInVFList) { Geometry::Box extendedBox=groupNode->getWorldBB(); extendedBox.resizeAbs(context.getCamera()->getNearPlane()); if (queries[i].getResult() > 0 || extendedBox.contains(context.getCamera()->getWorldOrigin()) ) { visibleGroupNodes.push_back(groupNode); } ++i; } context.getRenderingContext().popDepthBuffer(); delete [] queries; } std::deque<GeometryNode *> geoNodesInVisibleGroupNodes; // 3. collect all direct geometry-node children { struct GeoChildrenCollector:public NodeVisitor { GeoChildrenCollector(std::deque<GeometryNode*> & _geoNodes,FrameContext & _context): geoNodes(_geoNodes),firstNode(true),frameContext(_context) {} virtual ~GeoChildrenCollector() {} std::deque<GeometryNode*> & geoNodes; bool firstNode; FrameContext & frameContext; // ---|> NodeVisitor NodeVisitor::status enter(Node * _node) override { // if this is the first call, continue... if(firstNode) { firstNode=false; return CONTINUE_TRAVERSAL; } // else collect the geometry children and break the traversal. GeometryNode * gn=dynamic_cast<GeometryNode *>(_node); if(gn){ if(frameContext.getCamera()->testBoxFrustumIntersection( _node->getWorldBB()) != Geometry::Frustum::intersection_t::OUTSIDE) geoNodes.push_back(gn); } return BREAK_TRAVERSAL; } }; for (auto & visibleGroupNode : visibleGroupNodes) { GeoChildrenCollector vis(geoNodesInVisibleGroupNodes,context); (visibleGroupNode)->traverse(vis); } } // 4. clear screen and measure time drawing children float perfectRenderingTime=0; { context.getRenderingContext().clearScreen(clearColor); context.getRenderingContext().finish(); Util::Timer timer; timer.reset(); for (auto & geoNodesInVisibleGroupNode : geoNodesInVisibleGroupNodes) { context.displayNode((geoNodesInVisibleGroupNode),renderingFlags|USE_WORLD_MATRIX); } context.getRenderingContext().finish(); timer.stop(); perfectRenderingTime=timer.getMilliseconds(); } // // Test-costs: // float result=numTests!=0?(currentRenderingTime-perfectRenderingTime)/numTests:0; // currentRenderingTime-perfRendering: // float result=currentRenderingTime-perfectRenderingTime; float result=numPoly>0?perfectRenderingTime*1000/numPoly:0; // std::cout <<currentRenderingTime<<"-"<<perfectRenderingTime<<"="<<result<<"\t"<<numTests<<"\n"; // std::cout <<numVBO<<":"<<geoNodesInVisibleGroupNodes.size()<<"\n"; // float result=perfectRenderingTime; // values.push_back(currentRenderingTime); // values.push_back(perfectRenderingTime); values->push_back(new Util::_NumberAttribute<float>(result)); // if(mode==SINGLE_VALUE){ // if(result>renderingTime || renderingTime==0) // renderingTime=result; // }else{ // values.push_back(result); // } // renderingTime=0; }
void ParticleBillboardRenderer::operator()(ParticleSystemNode * psys, FrameContext & context, const RenderParam & rp) { if(rp.getFlag(NO_GEOMETRY)) { return; } const auto & worldToCamera = context.getRenderingContext().getMatrix_worldToCamera(); const auto cameraToWorld = worldToCamera.inverse(); const auto halfRight = cameraToWorld.transformDirection(context.getWorldRightVector() * 0.5f); const auto halfUp = cameraToWorld.transformDirection(context.getWorldUpVector() * 0.5f); // 2. just update position for each particle and render // render particles const uint32_t count = psys->getParticleCount(); Rendering::VertexDescription vertexDesc; const Rendering::VertexAttribute & posAttrib = vertexDesc.appendPosition3D(); const Rendering::VertexAttribute & colorAttrib = vertexDesc.appendColorRGBAByte(); const Rendering::VertexAttribute & texCoordAttrib = vertexDesc.appendTexCoord(); // The usage of a cache for the mesh has been tested. Reusing a preallocated mesh is not faster. Util::Reference<Rendering::Mesh> mesh = new Rendering::Mesh(vertexDesc, 4 * count, 6 * count); mesh->setDataStrategy(Rendering::SimpleMeshDataStrategy::getPureLocalStrategy()); Rendering::MeshIndexData & indexData = mesh->openIndexData(); Rendering::MeshVertexData & vertexData = mesh->openVertexData(); Util::Reference<Rendering::PositionAttributeAccessor> positionAccessor = Rendering::PositionAttributeAccessor::create(vertexData, posAttrib.getNameId()); Util::Reference<Rendering::ColorAttributeAccessor> colorAccessor = Rendering::ColorAttributeAccessor::create(vertexData, colorAttrib.getNameId()); Util::Reference<Rendering::TexCoordAttributeAccessor> texCoordAccessor = Rendering::TexCoordAttributeAccessor::create(vertexData, texCoordAttrib.getNameId()); uint32_t * indices = indexData.data(); uint_fast32_t index = 0; for(const auto & p : psys->getParticles()) { const Geometry::Vec3f upOffset = halfUp * p.size.getHeight(); const Geometry::Vec3f rightOffset = halfRight * p.size.getWidth(); colorAccessor->setColor(index + 0, p.color); texCoordAccessor->setCoordinate(index + 0, Geometry::Vec2f(0.0f, 0.0f)); positionAccessor->setPosition(index + 0, p.position + upOffset - rightOffset); colorAccessor->setColor(index + 1, p.color); texCoordAccessor->setCoordinate(index + 1, Geometry::Vec2f(0.0f, 1.0f)); positionAccessor->setPosition(index + 1, p.position - upOffset - rightOffset); colorAccessor->setColor(index + 2, p.color); texCoordAccessor->setCoordinate(index + 2, Geometry::Vec2f(1.0f, 1.0f)); positionAccessor->setPosition(index + 2, p.position - upOffset + rightOffset); colorAccessor->setColor(index + 3, p.color); texCoordAccessor->setCoordinate(index + 3, Geometry::Vec2f(1.0f, 0.0f)); positionAccessor->setPosition(index + 3, p.position + upOffset + rightOffset); *indices++ = index + 0; *indices++ = index + 1; *indices++ = index + 3; *indices++ = index + 1; *indices++ = index + 2; *indices++ = index + 3; index += 4; } indexData.markAsChanged(); indexData.updateIndexRange(); vertexData.markAsChanged(); vertexData.updateBoundingBox(); context.displayMesh(mesh.get()); }
void VSThreadPool::runTasks(VSThreadPool *owner, std::atomic<bool> &stop) { #ifdef VS_TARGET_CPU_X86 if (!vs_isMMXStateOk()) vsFatal("Bad MMX state detected after creating new thread"); #endif #ifdef VS_TARGET_OS_WINDOWS if (!vs_isFPUStateOk()) vsWarning("Bad FPU state detected after creating new thread"); if (!vs_isSSEStateOk()) vsFatal("Bad SSE state detected after creating new thread"); #endif std::unique_lock<std::mutex> lock(owner->lock); while (true) { bool ranTask = false; ///////////////////////////////////////////////////////////////////////////////////////////// // Go through all tasks from the top (oldest) and process the first one possible for (std::list<PFrameContext>::iterator iter = owner->tasks.begin(); iter != owner->tasks.end(); ++iter) { FrameContext *mainContext = iter->get(); FrameContext *leafContext = nullptr; ///////////////////////////////////////////////////////////////////////////////////////////// // Handle the output tasks if (mainContext->frameDone && mainContext->returnedFrame) { PFrameContext mainContextRef(*iter); owner->tasks.erase(iter); owner->returnFrame(mainContextRef, mainContext->returnedFrame); ranTask = true; break; } if (mainContext->frameDone && mainContext->hasError()) { PFrameContext mainContextRef(*iter); owner->tasks.erase(iter); owner->returnFrame(mainContextRef, mainContext->getErrorMessage()); ranTask = true; break; } bool hasLeafContext = mainContext->returnedFrame || mainContext->hasError(); if (hasLeafContext) { leafContext = mainContext; mainContext = mainContext->upstreamContext.get(); } VSNode *clip = mainContext->clip; int filterMode = clip->filterMode; ///////////////////////////////////////////////////////////////////////////////////////////// // This part handles the locking for the different filter modes bool parallelRequestsNeedsUnlock = false; if (filterMode == fmUnordered) { // already busy? if (!clip->serialMutex.try_lock()) continue; } else if (filterMode == fmSerial) { // already busy? if (!clip->serialMutex.try_lock()) continue; // no frame in progress? if (clip->serialFrame == -1) { clip->serialFrame = mainContext->n; // } else if (clip->serialFrame != mainContext->n) { clip->serialMutex.unlock(); continue; } // continue processing the already started frame } else if (filterMode == fmParallel) { std::lock_guard<std::mutex> lock(clip->concurrentFramesMutex); // is the filter already processing another call for this frame? if so move along if (clip->concurrentFrames.count(mainContext->n)) { continue; } else { clip->concurrentFrames.insert(mainContext->n); } } else if (filterMode == fmParallelRequests) { std::lock_guard<std::mutex> lock(clip->concurrentFramesMutex); // is the filter already processing another call for this frame? if so move along if (clip->concurrentFrames.count(mainContext->n)) { continue; } else { // do we need the serial lock since all frames will be ready this time? // check if we're in the arAllFramesReady state so we need additional locking if (mainContext->numFrameRequests == 1) { if (!clip->serialMutex.try_lock()) continue; parallelRequestsNeedsUnlock = true; clip->concurrentFrames.insert(mainContext->n); } } } ///////////////////////////////////////////////////////////////////////////////////////////// // Remove the context from the task list PFrameContext mainContextRef; PFrameContext leafContextRef; if (hasLeafContext) { leafContextRef = *iter; mainContextRef = leafContextRef->upstreamContext; } else { mainContextRef = *iter; } owner->tasks.erase(iter); ///////////////////////////////////////////////////////////////////////////////////////////// // Figure out the activation reason VSActivationReason ar = arInitial; bool skipCall = false; // Used to avoid multiple error calls for the same frame request going into a filter if ((hasLeafContext && leafContext->hasError()) || mainContext->hasError()) { ar = arError; skipCall = mainContext->setError(leafContext->getErrorMessage()); --mainContext->numFrameRequests; } else if (hasLeafContext && leafContext->returnedFrame) { if (--mainContext->numFrameRequests > 0) ar = arFrameReady; else ar = arAllFramesReady; mainContext->availableFrames.insert(std::make_pair(NodeOutputKey(leafContext->clip, leafContext->n, leafContext->index), leafContext->returnedFrame)); mainContext->lastCompletedN = leafContext->n; mainContext->lastCompletedNode = leafContext->node; } assert(mainContext->numFrameRequests >= 0); bool hasExistingRequests = !!mainContext->numFrameRequests; ///////////////////////////////////////////////////////////////////////////////////////////// // Do the actual processing lock.unlock(); VSFrameContext externalFrameCtx(mainContextRef); assert(ar == arError || !mainContext->hasError()); PVideoFrame f; if (!skipCall) f = clip->getFrameInternal(mainContext->n, ar, externalFrameCtx); ranTask = true; bool frameProcessingDone = f || mainContext->hasError(); if (mainContext->hasError() && f) vsFatal("A frame was returned by %s but an error was also set, this is not allowed", clip->name.c_str()); ///////////////////////////////////////////////////////////////////////////////////////////// // Unlock so the next job can run on the context if (filterMode == fmUnordered) { clip->serialMutex.unlock(); } else if (filterMode == fmSerial) { if (frameProcessingDone) clip->serialFrame = -1; clip->serialMutex.unlock(); } else if (filterMode == fmParallel) { std::lock_guard<std::mutex> lock(clip->concurrentFramesMutex); clip->concurrentFrames.erase(mainContext->n); } else if (filterMode == fmParallelRequests) { std::lock_guard<std::mutex> lock(clip->concurrentFramesMutex); clip->concurrentFrames.erase(mainContext->n); if (parallelRequestsNeedsUnlock) clip->serialMutex.unlock(); } ///////////////////////////////////////////////////////////////////////////////////////////// // Handle frames that were requested bool requestedFrames = !externalFrameCtx.reqList.empty() && !frameProcessingDone; lock.lock(); if (requestedFrames) { for (auto &reqIter : externalFrameCtx.reqList) owner->startInternal(reqIter); externalFrameCtx.reqList.clear(); } if (frameProcessingDone) owner->allContexts.erase(NodeOutputKey(mainContext->clip, mainContext->n, mainContext->index)); ///////////////////////////////////////////////////////////////////////////////////////////// // Propagate status to other linked contexts // CHANGES mainContextRef!!! if (mainContext->hasError() && !hasExistingRequests && !requestedFrames) { PFrameContext n; do { n = mainContextRef->notificationChain; if (n) { mainContextRef->notificationChain.reset(); n->setError(mainContextRef->getErrorMessage()); } if (mainContextRef->upstreamContext) { owner->startInternal(mainContextRef); } if (mainContextRef->frameDone) { owner->returnFrame(mainContextRef, mainContextRef->getErrorMessage()); } } while ((mainContextRef = n)); } else if (f) { if (hasExistingRequests || requestedFrames) vsFatal("A frame was returned at the end of processing by %s but there are still outstanding requests", clip->name.c_str()); PFrameContext n; do { n = mainContextRef->notificationChain; if (n) mainContextRef->notificationChain.reset(); if (mainContextRef->upstreamContext) { mainContextRef->returnedFrame = f; owner->startInternal(mainContextRef); } if (mainContextRef->frameDone) owner->returnFrame(mainContextRef, f); } while ((mainContextRef = n)); } else if (hasExistingRequests || requestedFrames) { // already scheduled, do nothing } else { vsFatal("No frame returned at the end of processing by %s", clip->name.c_str()); } break; } if (!ranTask || owner->activeThreadCount() > owner->threadCount()) { --owner->activeThreads; if (stop) { lock.unlock(); break; } ++owner->idleThreads; owner->newWork.wait(lock); --owner->idleThreads; ++owner->activeThreads; } } }
State::stateResult_t TwinPartitionsRenderer::doEnableState(FrameContext & context, Node *, const RenderParam & rp) { if (rp.getFlag(SKIP_RENDERER)) { return State::STATE_SKIPPED; } if (data == nullptr) { // Invalid information. => Fall back to standard rendering. return State::STATE_SKIPPED; } const Geometry::Vec3f pos = context.getCamera()->getWorldOrigin(); // Check if cached cell can be used. if (currentCell == INVALID_CELL || !data->cells[currentCell].bounds.contains(pos)) { // Search the cell that contains the camera position. bool cellFound = false; const uint32_t cellCount = data->cells.size(); for (uint_fast32_t cell = 0; cell < cellCount; ++cell) { if (data->cells[cell].bounds.contains(pos)) { // #ifdef MINSG_EXT_OUTOFCORE // // Set new priorities if the current cell changes. // if (currentCell != cell) { // // Low priority for old depth mesh. // if (currentCell != INVALID_CELL) { // for(std::vector<uint32_t>::const_iterator it = data->cells[currentCell].surroundingIds.begin(); it != data->cells[currentCell].surroundingIds.end(); ++it) { // Rendering::Mesh * depthMesh = data->depthMeshes[*it].get(); // OutOfCore::getCacheManager().setUserPriority(depthMesh, 0); // } // } // // High priority for new depth mesh. // { // for(std::vector<uint32_t>::const_iterator it = data->cells[cell].surroundingIds.begin(); it != data->cells[cell].surroundingIds.end(); ++it) { // Rendering::Mesh * depthMesh = data->depthMeshes[*it].get(); // OutOfCore::getCacheManager().setUserPriority(depthMesh, 20); // } // } // // Low priority for meshes visible from the old cell. // if (currentCell != INVALID_CELL) { // const uint32_t oldVisibleSetIndex = data->cells[currentCell].visibleSetId; // const PartitionsData::visible_set_t & oldVisibleSet = data->visibleSets[oldVisibleSetIndex]; // for (PartitionsData::visible_set_t::const_iterator it = oldVisibleSet.begin(); it != oldVisibleSet.end(); ++it) { // Rendering::Mesh * mesh = data->objects[it->second].get(); // OutOfCore::getCacheManager().setUserPriority(mesh, 0); // } // } // // High priority for meshes visible from the new cell. // { // const uint32_t visibleSetIndex = data->cells[cell].visibleSetId; // const PartitionsData::visible_set_t & visibleSet = data->visibleSets[visibleSetIndex]; // for (PartitionsData::visible_set_t::const_iterator it = visibleSet.begin(); it != visibleSet.end(); ++it) { // Rendering::Mesh * mesh = data->objects[it->second].get(); // OutOfCore::getCacheManager().setUserPriority(mesh, 10); // } // } // } // #endif /* MINSG_EXT_OUTOFCORE */ // Load new textures. textures.clear(); if(drawTexturedDepthMeshes) { textures.reserve(6); for(auto & elem : data->cells[cell].surroundingIds) { const std::string & textureFile = data->textureFiles[elem]; Util::Reference<Rendering::Texture> texture = Rendering::Serialization::loadTexture(Util::FileName(textureFile)); if(texture == nullptr) { WARN("Failed to load texture for depth mesh."); } else { textures.push_back(texture); } } } currentCell = cell; cellFound = true; break; } } if (!cellFound) { // Camera outside view space. currentCell = INVALID_CELL; return State::STATE_SKIPPED; } } uint32_t renderedTriangles = 0; const uint32_t visibleSetIndex = data->cells[currentCell].visibleSetId; const PartitionsData::visible_set_t & visibleSet = data->visibleSets[visibleSetIndex]; if(drawTexturedDepthMeshes) { for(auto & elem : data->cells[currentCell].surroundingIds) { Rendering::Mesh * depthMesh = data->depthMeshes[elem].get(); // Count the depth meshes here already. renderedTriangles += depthMesh->getPrimitiveCount(); } } const Geometry::Frustum & frustum = context.getCamera()->getFrustum(); /*float minDist = 9999999.0f; float maxDist = 0.0f; float prioSum = 0.0f; uint_fast32_t objectCounter = 0; for (PartitionsData::visible_set_t::const_iterator it = visibleSet.begin(); it != visibleSet.end(); ++it) { Rendering::Mesh * mesh = data->objects[it->second].get(); float dist = mesh->getBoundingBox().getDistance(pos); minDist = std::min(dist, minDist); maxDist = std::max(dist, maxDist); if(objectCounter < 10) { prioSum += it->first; ++objectCounter; } } std::ofstream output("twinOutput.tsv", ios_base::out | ios_base::app); output << currentCell << '\t' << data->cells[currentCell].bounds.getDiameter() << '\t' << (data->cells[currentCell].bounds.getCenter() - pos).length() << '\t' << visibleSet.begin()->first << '\t' << visibleSet.rbegin()->first << '\t' << minDist << '\t' << maxDist << '\t' << prioSum << '\t'; for(std::vector<uint32_t>::const_iterator it = data->cells[currentCell].surroundingIds.begin(); it != data->cells[currentCell].surroundingIds.end(); ++it) { Rendering::Mesh * depthMesh = data->depthMeshes[*it].get(); const Geometry::Box & depthMeshBB = depthMesh->getBoundingBox(); const Geometry::Vec3f originalDirection = (depthMeshBB.getCenter() - data->cells[currentCell].bounds.getCenter()).normalize(); const Geometry::Vec3f currentDirection = (depthMeshBB.getCenter() - pos).normalize(); const float angle = currentDirection.dot(originalDirection); const bool visible = (frustum.isBoxInFrustum(depthMeshBB) != Frustum::OUTSIDE); output << '\t'; if(visible) { output << angle ; } else { output << "NA"; } } for(uint_fast32_t i = data->cells[currentCell].surroundingIds.size(); i < 6; ++i) { output << "\tNA"; } output << std::endl; output.close();*/ for (auto & elem : visibleSet) { Rendering::Mesh * mesh = data->objects[elem.second].get(); if (conditionalFrustumTest(frustum, mesh->getBoundingBox(), rp)) { context.displayMesh(mesh); renderedTriangles += mesh->getPrimitiveCount(); } if(rp.getFlag(BOUNDING_BOXES)) { Rendering::drawWireframeBox(context.getRenderingContext(), mesh->getBoundingBox(), Util::ColorLibrary::RED); } if (maxRuntime != 0 && renderedTriangles >= maxRuntime) { break; } } // Draw the textured depth meshes at the end. if(drawTexturedDepthMeshes) { context.getRenderingContext().pushAndSetPolygonOffset(Rendering::PolygonOffsetParameters(polygonOffsetFactor, polygonOffsetUnits)); context.getRenderingContext().pushAndSetShader(getTDMShader()); context.getRenderingContext().pushTexture(0); auto texIt = textures.begin(); for(auto & elem : data->cells[currentCell].surroundingIds) { Rendering::Mesh * depthMesh = data->depthMeshes[elem].get(); Rendering::Texture * texture = texIt->get(); context.getRenderingContext().setTexture(0, texture); if (conditionalFrustumTest(frustum, depthMesh->getBoundingBox(), rp)) { context.displayMesh(depthMesh); } ++texIt; } context.getRenderingContext().popTexture(0); context.getRenderingContext().popShader(); context.getRenderingContext().popPolygonOffset(); } return State::STATE_SKIP_RENDERING; }
void ShaderState::setUniform(FrameContext & context, const Rendering::Uniform & value) { setUniform(value); shader->setUniform(context.getRenderingContext(),value,true,true); // warn if uniform is not defined in shader and force at least one try on applying the uniform }
State::stateResult_t VisibilitySubdivisionRenderer::doEnableState( FrameContext & context, Node *, const RenderParam & rp) { if (rp.getFlag(SKIP_RENDERER)) { return State::STATE_SKIPPED; } if (viSu == nullptr) { // Invalid information. => Fall back to standard rendering. return State::STATE_SKIPPED; } // [AccumRendering] if(accumRenderingEnabled){ // camera moved? -> start new frame const Geometry::Matrix4x4 & newCamMat=context.getCamera()->getWorldTransformationMatrix(); if(newCamMat!=lastCamMatrix){ lastCamMatrix = newCamMat; startRuntime=0; } }else{ startRuntime=0; } // ---- uint32_t renderedTriangles = 0; if (hold) { for (const auto & object : holdObjects) { if (debugOutput) { debugDisplay(renderedTriangles, object, context, rp); } else { context.displayNode(object, rp); } } return State::STATE_SKIP_RENDERING; } Geometry::Vec3 pos = context.getCamera()->getWorldOrigin(); bool refreshCache = false; // Check if cached cell can be used. if (currentCell == nullptr || !currentCell->getBB().contains(pos)) { currentCell = viSu->getNodeAtPosition(pos); refreshCache = true; } if (currentCell == nullptr || !currentCell->isLeaf()) { // Invalid information. => Fall back to standard rendering. return State::STATE_SKIPPED; } if (refreshCache) { try { const auto & vv = getVV(currentCell); const uint32_t maxIndex = vv.getIndexCount(); objects.clear(); objects.reserve(maxIndex); for(uint_fast32_t index = 0; index < maxIndex; ++index) { if(vv.getBenefits(index) == 0) { continue; } const VisibilityVector::costs_t costs = vv.getCosts(index); const VisibilityVector::benefits_t benefits = vv.getBenefits(index); const float score = static_cast<float>(costs) / static_cast<float>(benefits); objects.emplace_back(score, vv.getNode(index)); } } catch(...) { // Invalid information. => Fall back to standard rendering. return State::STATE_SKIPPED; } if (displayTexturedDepthMeshes) { #ifdef MINSG_EXT_OUTOFCORE for (const auto & depthMesh : depthMeshes) { OutOfCore::getCacheManager().setUserPriority(depthMesh.get(), 0); } #endif /* MINSG_EXT_OUTOFCORE */ depthMeshes.clear(); depthMeshes.reserve(6); textures.clear(); textures.reserve(6); const std::string dmDirectionStrings[6] = { "-dir_x1_y0_z0", "-dir_x-1_y0_z0", "-dir_x0_y1_z0", "-dir_x0_y-1_z0", "-dir_x0_y0_z1", "-dir_x0_y0_z-1" }; for (auto & dmDirectionString : dmDirectionStrings) { Util::GenericAttribute * attrib = currentCell->getAttribute("DepthMesh" + dmDirectionString); if (attrib == nullptr) { continue; } Util::FileName dmMeshPath(attrib->toString()); Util::Reference<Rendering::Mesh> dmMesh; #ifdef MINSG_EXT_OUTOFCORE Util::GenericAttribute * bbAttrib = currentCell->getAttribute("DepthMesh" + dmDirectionString + "-bounds"); if (bbAttrib == nullptr) { WARN("Found depth mesh with no bounding box."); continue; } std::vector<float> bbValues = Util::StringUtils::toFloats(bbAttrib->toString()); FAIL_IF(bbValues.size() != 6); const Geometry::Box meshBB(Geometry::Vec3(bbValues[0], bbValues[1], bbValues[2]), bbValues[3], bbValues[4], bbValues[5]); dmMesh = OutOfCore::addMesh(dmMeshPath, meshBB); #else /* MINSG_EXT_OUTOFCORE */ dmMesh = Rendering::Serialization::loadMesh(dmMeshPath); #endif /* MINSG_EXT_OUTOFCORE */ depthMeshes.emplace_back(dmMesh); // Count the depth mesh here already. renderedTriangles += dmMesh->getPrimitiveCount(); Util::GenericAttribute * texAttrib = currentCell->getAttribute("Texture" + dmDirectionString); if (texAttrib == nullptr) { continue; } Util::FileName texturePath(texAttrib->toString()); Util::Reference<Rendering::Texture> texture = Rendering::Serialization::loadTexture(texturePath); if (texture.isNull()) { WARN("Loading texture for depth mesh failed."); continue; } textures.emplace_back(texture); } } } const Geometry::Frustum & frustum = context.getCamera()->getFrustum(); holdObjects.clear(); holdObjects.reserve(objects.size()); std::sort(objects.begin(), objects.end()); for(const auto & ratioObjectPair : objects) { object_ptr o = ratioObjectPair.second; #ifdef MINSG_EXT_OUTOFCORE OutOfCore::getCacheManager().setUserPriority(o->getMesh(), 5); #endif /* MINSG_EXT_OUTOFCORE */ if (conditionalFrustumTest(frustum, o->getWorldBB(), rp)) { // [AccumRendering] // skip geometry rendered in the last frame if(renderedTriangles<startRuntime){ renderedTriangles += o->getTriangleCount(); continue; } // ---- if (debugOutput) { debugDisplay(renderedTriangles, o, context, rp); } else { context.displayNode(o, rp); renderedTriangles += o->getTriangleCount(); } holdObjects.push_back(o); } if (maxRuntime != 0 && renderedTriangles >= startRuntime+maxRuntime) { break; } } // Draw the textured depth meshes at the end. if (displayTexturedDepthMeshes) { context.getRenderingContext().pushAndSetPolygonOffset(Rendering::PolygonOffsetParameters(polygonOffsetFactor, polygonOffsetUnits)); auto texIt = textures.cbegin(); for (const auto & depthMesh : depthMeshes) { context.getRenderingContext().pushAndSetShader(getTDMShader()); context.getRenderingContext().pushAndSetTexture(0,texIt->get()); if (conditionalFrustumTest(frustum, depthMesh->getBoundingBox(), rp)) { context.displayMesh(depthMesh.get()); } context.getRenderingContext().popTexture(0); context.getRenderingContext().popShader(); #ifdef MINSG_EXT_OUTOFCORE OutOfCore::getCacheManager().setUserPriority(depthMesh.get(), 2); #endif /* MINSG_EXT_OUTOFCORE */ ++texIt; } context.getRenderingContext().popPolygonOffset(); } // [AccumRendering] startRuntime=renderedTriangles; // ---- return State::STATE_SKIP_RENDERING; }
State::stateResult_t PhotonRenderer::doEnableState(FrameContext & context, Node * node, const RenderParam & rp){ #ifdef MINSG_THESISSTANISLAW_GATHER_STATISTICS Rendering::RenderingContext::finish(); _timer.reset(); #endif // MINSG_THESISSTANISLAW_GATHER_STATISTICS if(!_photonSampler){ WARN("No PhotonSampler present in PhotonRenderer!"); return State::stateResult_t::STATE_SKIPPED; } if(!_approxScene){ WARN("No approximated Scene present in PhotonRenderer!"); return State::stateResult_t::STATE_SKIPPED; } auto& rc = context.getRenderingContext(); rc.setImmediateMode(true); rc.applyChanges(); if(_fboChanged){ if(!initializeFBO(rc)){ WARN("Could not initialize FBO for PhotonRenderer!"); return State::stateResult_t::STATE_SKIPPED; } _fboChanged = false; } RenderParam newRp; rc.pushAndSetFBO(_fbo.get()); rc.clearDepth(1.0f); rc.applyChanges(); for(int32_t i = 0; i < _photonSampler->getPhotonNumber(); i++){ _fbo->setDrawBuffers(2); rc.pushAndSetShader(_indirectLightShader.get()); _lightPatchRenderer->bindTBO(rc, true, false); _photonSampler->bindPhotonBuffer(1); context.pushAndSetCamera(_photonCamera.get()); _indirectLightShader->setUniform(rc, Rendering::Uniform("photonID", i)); rc.clearDepth(1.0f); rc.clearColor(Util::Color4f(0.f, 0.f, 0.f, 0.f)); _approxScene->display(context, newRp); context.popCamera(); _photonSampler->unbindPhotonBuffer(1); _lightPatchRenderer->unbindTBO(rc); rc.popShader(); // Accumulate all pixel values in one photon _fbo->setDrawBuffers(0); rc.pushAndSetShader(_accumulationShader.get()); _accumulationShader->setUniform(rc, Rendering::Uniform("photonID", i)); _accumulationShader->setUniform(rc, Rendering::Uniform("samplingWidth", static_cast<int>(_samplingWidth))); _accumulationShader->setUniform(rc, Rendering::Uniform("samplingHeight", static_cast<int>(_samplingHeight))); _photonSampler->bindPhotonBuffer(1); rc.clearDepth(1.0f); Rendering::TextureUtils::drawTextureToScreen(rc, Geometry::Rect_i(0, 0, _samplingWidth, _samplingHeight), *_indirectLightTexture.get(), Geometry::Rect_f(0.0f, 0.0f, 1.0f, 1.0f)); _photonSampler->unbindPhotonBuffer(1); rc.popShader(); } rc.popFBO(); rc.setImmediateMode(false); // rc.pushAndSetShader(nullptr); // Rendering::TextureUtils::drawTextureToScreen(rc, Geometry::Rect_i(0, 0, _samplingWidth, _samplingHeight), *(_indirectLightTexture.get()), Geometry::Rect_f(0.0f, 0.0f, 1.0f, 1.0f)); // rc.popShader(); // return State::stateResult_t::STATE_SKIP_RENDERING; #ifdef MINSG_THESISSTANISLAW_GATHER_STATISTICS Rendering::RenderingContext::finish(); _timer.stop(); auto& stats = context.getStatistics(); Statistics::instance(stats).addPhotonRendererTime(stats, _timer.getMilliseconds()); #endif // MINSG_THESISSTANISLAW_GATHER_STATISTICS return State::stateResult_t::STATE_OK; }
//! ---|> [State] State::stateResult_t CHCRenderer::doEnableState(FrameContext & context,Node * rootNode, const RenderParam & rp){ if(rp.getFlag(SKIP_RENDERER)) return State::STATE_SKIPPED; if(debugShowVisible){ std::deque<Node *> nodes; { const auto children = getChildNodes(rootNode); nodes.insert(nodes.end(), children.begin(), children.end()); } while(!nodes.empty()){ Node& node = *nodes.front(); nodes.pop_front(); auto& nodeInfo = getNodeInfo(node); if(nodeInfo.frameNr == frameNr && nodeInfo.visible){ if(node.isClosed()) context.displayNode(&node,rp); else { const auto children = getChildNodes(&node); nodes.insert(nodes.end(), children.begin(), children.end()); } } } }else{ ++frameNr; // used for statistics unsigned int stat_numTests = 0; unsigned int stat_numTestsInvisible = 0; unsigned int stat_numTestsVisible = 0; Statistics & statistics = context.getStatistics(); RenderParam childParam = rp + USE_WORLD_MATRIX; const auto& camera = *context.getCamera(); const Geometry::Vec3 camPos = camera.getWorldOrigin(); const float bbEnlargement = camera.getNearPlane(); NodeDistancePriorityQueue_F2B distanceQueue(camPos); std::queue<std::pair<Rendering::OcclusionQuery, Node*>> queryQueue; const auto traverseNode = [&](Node& node){ if( node.isClosed() ){ // leaf node context.displayNode(&node, childParam ); }else{ for(auto & child : getChildNodes(&node)) distanceQueue.push(child); } }; const auto pullUpVisibility = [&](Node& node){ for(Node* n=&node; n&&n!=rootNode; n = n->getParent()){ auto& nodeInfo = getNodeInfo(*n); if(nodeInfo.frameNr == frameNr && nodeInfo.visible) break; nodeInfo.visible = true; nodeInfo.frameNr = frameNr; }; }; const auto startQuery = [&](Node& node,const Geometry::Box& worldBoundingBox){ Rendering::OcclusionQuery query; Rendering::OcclusionQuery::enableTestMode(context.getRenderingContext()); query.begin(); Rendering::drawAbsBox(context.getRenderingContext(), worldBoundingBox ); query.end(); Rendering::OcclusionQuery::disableTestMode(context.getRenderingContext()); return std::make_pair(std::move(query),&node); }; traverseNode(*rootNode); while(!distanceQueue.empty() || !queryQueue.empty()){ // ---- PART 1: process finished occlusion queries while( !queryQueue.empty() && (distanceQueue.empty() || queryQueue.front().first.isResultAvailable()) ){ if( queryQueue.front().first.getResult()>0 ){ ++stat_numTestsVisible; statistics.pushEvent( Statistics::EVENT_TYPE_END_TEST_VISIBLE, 1); Node& node = *queryQueue.front().second; pullUpVisibility(node); auto& nodeInfo = getNodeInfo( node ); if( !nodeInfo.wasVisible ) traverseNode(node); }else{ ++stat_numTestsInvisible; statistics.pushEvent( Statistics::EVENT_TYPE_END_TEST_INVISIBLE, 1); } queryQueue.pop(); } // ---- PART 2: tree traversal if( !distanceQueue.empty() ){ Node& node = *distanceQueue.top(); distanceQueue.pop(); const Geometry::Box worldBoundingBox = node.getWorldBB(); if (camera.testBoxFrustumIntersection( node.getWorldBB()) == Geometry::Frustum::intersection_t::OUTSIDE) { continue; } Geometry::Box enlargedBox = worldBoundingBox; enlargedBox.resizeAbs( bbEnlargement ); if( enlargedBox.contains(camPos) ){ pullUpVisibility(node); traverseNode(node); continue; } auto& nodeInfo = getNodeInfo( node ); // identify previously visible nodes const bool wasVisible = nodeInfo.frameNr == frameNr-1 && nodeInfo.visible; nodeInfo.wasVisible = wasVisible; // reset node's visibility flag nodeInfo.visible = false; // update node's visited flag nodeInfo.frameNr = frameNr; // test leafs and previously invisible nodes if( node.isClosed() || !wasVisible){ // on testing front // start test ++stat_numTests; statistics.pushEvent(Statistics::EVENT_TYPE_START_TEST, 1); queryQueue.push( std::move(startQuery(node,worldBoundingBox)) ); } // traverse a node unless it was invisible if( wasVisible ) traverseNode( node ); } } statistics.addValue(OcclusionCullingStatistics::instance(statistics).getOccTestVisibleCounter(), stat_numTestsVisible); statistics.addValue(OcclusionCullingStatistics::instance(statistics).getOccTestInvisibleCounter(), stat_numTestsInvisible); statistics.addValue(OcclusionCullingStatistics::instance(statistics).getOccTestCounter(), stat_numTests); } // std::cout << std::endl; return State::STATE_SKIP_RENDERING; }
//! ---|> [State] void ShaderState::doDisableState(FrameContext & context, Node *, const RenderParam & /*rp*/) { // restore old shader context.getRenderingContext().popShader(); }
State::stateResult_t SkyboxState::doEnableState(FrameContext & context, Node *, const RenderParam & rp) { if (rp.getFlag(NO_GEOMETRY)) { return State::STATE_SKIPPED; } Vec3 pos = context.getCamera()->getWorldOrigin(); Geometry::Matrix4x4 matrix; matrix.translate(pos); context.getRenderingContext().pushMatrix_modelToCamera(); context.getRenderingContext().multMatrix_modelToCamera(matrix); context.getRenderingContext().pushTexture(0); context.getRenderingContext().pushAndSetDepthBuffer(Rendering::DepthBufferParameters(true, false, Rendering::Comparison::LESS)); context.getRenderingContext().pushAndSetLighting(Rendering::LightingParameters(false)); Geometry::Box box(Geometry::Vec3(0.0f, 0.0f, 0.0f), sideLength); for (uint_fast8_t side = 0; side < 6; ++side) { if (!textures[side].isNull()) { context.getRenderingContext().setTexture(0,textures[side].get()); const Geometry::corner_t * corners = Geometry::Helper::getCornerIndices(static_cast<Geometry::side_t> (side)); // Reverse the corner order because we need the inner surface. Rendering::drawQuad(context.getRenderingContext(), box.getCorner(corners[1]), box.getCorner(corners[0]), box.getCorner(corners[3]), box.getCorner(corners[2]),Util::ColorLibrary::WHITE); } } context.getRenderingContext().popLighting(); context.getRenderingContext().popDepthBuffer(); context.getRenderingContext().popTexture(0); context.getRenderingContext().popMatrix_modelToCamera(); return State::STATE_OK; }
//! (internal) Main culling method State::stateResult_t OccRenderer::performCulling(FrameContext & context,Node * rootNode, const RenderParam & rp){ if(mode==MODE_UNCONDITIONED){ // destroy temporal coherence frameNumber++; } frameNumber++; updateNodeInformation(context,rootNode); // ??? // used for statistics unsigned int tests=0; int testsVisible=0; int testsInvisible=0; int occludedGeometryNodes=0; Statistics & statistics = context.getStatistics(); RenderParam childParam = rp + USE_WORLD_MATRIX; const Geometry::Vec3 camPos=context.getCamera()->getWorldOrigin(); // NodeDistancePriorityQueue_F2B distanceQueue(camPos); NodeDistancePriorityQueue_F2B distanceQueue(camPos); { // add root node's children const auto children = getChildNodes(rootNode); for(auto & child : children) { distanceQueue.push(child); } } std::queue<std::pair<Rendering::OcclusionQuery, Node *>> queryQueue; // bool wasIdle=true; int idleCount=0; while ( (!distanceQueue.empty()) || (!queryQueue.empty() ) ) { bool idle=true; while ( !queryQueue.empty() && queryQueue.front().first.isResultAvailable() ) { idle=false; const OcclusionQuery & currentQuery = queryQueue.front().first; unsigned int result = currentQuery.getResult(); Node * node = queryQueue.front().second; queryQueue.pop(); NodeInfo * nodeInfo = getNodeInfo(node); // Node is visible? if (result>0 ) { statistics.pushEvent(Statistics::EVENT_TYPE_END_TEST_VISIBLE,nodeInfo->getActualSubtreeComplexity()); testsVisible++; if (node->isClosed()) { // mark parents as visible Node * n=node; NodeInfo * ni=nodeInfo; while( ni->getVisibleFrameNumber()!=frameNumber){ ni->setVisibleFrameNumber(frameNumber); n=n->getParent(); if(n==nullptr || n==rootNode) break; ni=getNodeInfo(n); } } nodeInfo->setVisibleFrameNumber(frameNumber); // haben wir schon bearbeitet? -> Weiter... if (nodeInfo->getProcessedFrameNumber()==frameNumber) continue; //// if(node->visibleFrameNumber != (frameNumber-1)){ ???? // Process node processNode(context,node,nodeInfo,distanceQueue,childParam); //// } }// Node is fully occluded? else { testsInvisible++; statistics.pushEvent(Statistics::EVENT_TYPE_END_TEST_INVISIBLE, nodeInfo->getActualSubtreeComplexity()); // not processed yet? if (nodeInfo->getProcessedFrameNumber()!=frameNumber) { // TODO: Store and propagate in tree. occludedGeometryNodes+=collectNodesInFrustum<GeometryNode>(node,context.getCamera()->getFrustum()).size(); // mark the node as processed nodeInfo->setProcessedFrameNumber(frameNumber); } // In diesem Frame verdeckt worden? (dann haben wir nicht mit der Bearbeitung gewartet und // die Kinder liegen bereits in der distanceQueue; die sollten jetzt �bersprungen werden) if ( nodeInfo->getVisibleFrameNumber() == (frameNumber-1) ) { if (!node->isClosed()) { const auto children = getChildNodes(node); for(auto & child : children) { getNodeInfo(child)->setProcessedFrameNumber(frameNumber); // TODO: Check if ancestors have to be removed too. } } } } } // ... if ( ! distanceQueue.empty()) { idle=false; Node * node=distanceQueue.top(); NodeInfo * nodeInfo=getNodeInfo(node); distanceQueue.pop(); // If the current node has already been processed, then continue. if (nodeInfo->getProcessedFrameNumber()==frameNumber ) continue; // ||node->isEmpty() // // skip empty subtrees // does never happen (checked in processNode) // if( nodeInfo->getActualSubtreeComplexity() == 0){ // nullBoxes++; // continue; // } // Kamera innerhalb der (um die nearplane vergroesserte) Box? -> immer sichtbar // [2008-01-04_CJ] Bugfix: added nearplane addition; could result in dissapearing scene-parts. Geometry::Box extendedBox=node->getWorldBB(); //node->getGeometryBB(); extendedBox.resizeAbs(context.getCamera()->getNearPlane()); if (extendedBox.contains(camPos)) { nodeInfo->setVisibleFrameNumber(frameNumber); // Process node processNode(context,node,nodeInfo,distanceQueue,childParam); continue; } // !IS_INSIDE_FRUSTUM(node) if ( nodeInfo->getActualFrustumStatus()== Geometry::Frustum::OUTSIDE) continue; // [MODE_OPT_CULLING] For optimal culling, skip all unnecessary tests if(mode==MODE_OPT_CULLING && nodeInfo->getVisibleFrameNumber() == (frameNumber-1)){ nodeInfo->setVisibleFrameNumber(frameNumber); // Process node processNode(context,node,nodeInfo,distanceQueue,childParam); continue; } // if query reasonable // TODO: Something missing here? { // start occlusion test for the current node Rendering::OcclusionQuery query; Rendering::OcclusionQuery::enableTestMode(context.getRenderingContext()); query.begin(); Rendering::drawFastAbsBox(context.getRenderingContext(), node->getWorldBB()); query.end(); Rendering::OcclusionQuery::disableTestMode(context.getRenderingContext()); tests++; queryQueue.emplace(std::move(query), node); statistics.pushEvent(Statistics::EVENT_TYPE_START_TEST, nodeInfo->getActualSubtreeComplexity()); } // if the current node was visible in the last frame, // then assume that it is also visible in this frame and process it. if (nodeInfo->getVisibleFrameNumber() == (frameNumber-1) ) { // Process node processNode(context,node,nodeInfo,distanceQueue,childParam); } } // else{ // waiting // } if (idle) { statistics.pushEvent(Statistics::EVENT_TYPE_IDLE, idleCount++); } else { idleCount = 0; } } statistics.addValue(OcclusionCullingStatistics::instance(statistics).getOccTestCounter(), tests); statistics.addValue(OcclusionCullingStatistics::instance(statistics).getOccTestVisibleCounter(), testsVisible); statistics.addValue(OcclusionCullingStatistics::instance(statistics).getOccTestInvisibleCounter(), testsInvisible); statistics.addValue(OcclusionCullingStatistics::instance(statistics).getCulledGeometryNodeCounter(), occludedGeometryNodes); statistics.pushEvent(Statistics::EVENT_TYPE_FRAME_END,0.3); return State::STATE_SKIP_RENDERING; }
//! Distribute the budget based on the projected size and the primitive count of the child nodes. static void distributeProjectedSizeAndPrimitiveCount(double value, const Util::StringIdentifier & attributeId, Node * node, FrameContext & context) { static const Util::StringIdentifier primitiveCountId("PrimitiveCount"); struct PrimitiveCountAnnotationVisitor : public NodeVisitor { const Util::StringIdentifier & m_primitiveCountId; PrimitiveCountAnnotationVisitor(const Util::StringIdentifier & p_primitiveCountId) : m_primitiveCountId(p_primitiveCountId) { } virtual ~PrimitiveCountAnnotationVisitor() { } NodeVisitor::status leave(Node * _node) override { auto geoNode = dynamic_cast<GeometryNode *>(_node); uint32_t primitiveCount = 0; if(geoNode != nullptr) { const auto mesh = geoNode->getMesh(); primitiveCount = mesh == nullptr ? 0 : mesh->getPrimitiveCount(); } else { const auto children = getChildNodes(_node); for(const auto & child : children) { primitiveCount += child->getAttribute(m_primitiveCountId)->toUnsignedInt(); } } _node->setAttribute(m_primitiveCountId, Util::GenericAttribute::createNumber(primitiveCount)); return CONTINUE_TRAVERSAL; } }; const auto children = getChildNodes(node); // A tuple stores the primitive count, the node and its projected size. std::vector<std::tuple<uint32_t, Node *, float>> primitiveCountNodeProjSizeTuples; primitiveCountNodeProjSizeTuples.reserve(children.size()); double projSizeSum = 0.0; const Geometry::Rect_f screenRect(context.getRenderingContext().getWindowClientArea()); for(const auto & child : children) { if(!child->isAttributeSet(primitiveCountId)) { PrimitiveCountAnnotationVisitor visitor(primitiveCountId); child->traverse(visitor); } const auto primitiveCount = child->getAttribute(primitiveCountId)->toUnsignedInt(); // Clip the projected rect to the screen. auto projRect = context.getProjectedRect(child); projRect.clipBy(screenRect); const auto projSize = projRect.getArea(); projSizeSum += projSize; primitiveCountNodeProjSizeTuples.emplace_back(primitiveCount, child, projSize); } // Begin with the node with the lowest primitive count std::sort(primitiveCountNodeProjSizeTuples.begin(), primitiveCountNodeProjSizeTuples.end()); for(const auto & primitiveCountNodeProjSizeTuple : primitiveCountNodeProjSizeTuples) { const auto primitiveCount = std::get<0>(primitiveCountNodeProjSizeTuple); const auto projSize = std::get<2>(primitiveCountNodeProjSizeTuple); const auto projSizeFactor = projSize / projSizeSum; const auto primitiveAssignment = std::min(static_cast<uint32_t>(projSizeFactor * value), primitiveCount); setOrUpdateAttribute(std::get<1>(primitiveCountNodeProjSizeTuple), attributeId, primitiveAssignment); value -= primitiveAssignment; projSizeSum -= projSize; } }
//! ---|> Evaluator void VisibilityEvaluator::measure(FrameContext & context,Node & node,const Geometry::Rect & /*r*/) { // first pass - fill depth buffer. context.getRenderingContext().clearScreen(Util::Color4f(0.9f, 0.9f, 0.9f, 1.0f)); node.display(context,USE_WORLD_MATRIX|FRUSTUM_CULLING); if (getMaxValue()->toInt() == 0.0) { setMaxValue_i(collectNodes<GeometryNode>(&node).size()); } if (mode==SINGLE_VALUE) { // collect geometry nodes in frustum auto objectsInVFList = collectNodesInFrustum<GeometryNode>(&node, context.getCamera()->getFrustum()); for(const auto & geoNode : objectsInVFList) { objectsInVF[reinterpret_cast<uintptr_t>(geoNode)] = geoNode; } // setup occlusion queries int numQueries=objectsInVFList.size(); if (numQueries==0) return; auto queries = new Rendering::OcclusionQuery[numQueries]; { // optional second pass: test bounding boxes context.getRenderingContext().pushAndSetDepthBuffer(Rendering::DepthBufferParameters(true, true, Rendering::Comparison::LEQUAL)); context.getRenderingContext().pushAndSetPolygonOffset(Rendering::PolygonOffsetParameters(0.0f, -1.0f)); int i=0; // start queries Rendering::OcclusionQuery::enableTestMode(context.getRenderingContext()); for(const auto & geoNode : objectsInVFList) { queries[i].begin(); Rendering::drawAbsBox(context.getRenderingContext(), geoNode->getWorldBB() ); queries[i].end(); ++i; } Rendering::OcclusionQuery::disableTestMode(context.getRenderingContext()); i=0; // get results for(auto & geoNode : objectsInVFList) { if (queries[i].getResult() == 0 && !geoNode->getWorldBB().contains(context.getCamera()->getWorldOrigin()) ) { geoNode=nullptr; } ++i; } context.getRenderingContext().popPolygonOffset(); context.getRenderingContext().popDepthBuffer(); } { // third pass: test if nodes are exactly visible context.getRenderingContext().pushAndSetDepthBuffer(Rendering::DepthBufferParameters(true, true, Rendering::Comparison::EQUAL)); int i=0; // start queries for(const auto & geoNode : objectsInVFList) { if (!geoNode) continue; queries[i].begin(); // render object i context.displayNode(geoNode, USE_WORLD_MATRIX); queries[i].end(); ++i; } i=0; // get results for(const auto & geoNode : objectsInVFList) { if (!geoNode) continue; if (queries[i].getResult() > 0) { visibleObjects[reinterpret_cast<uintptr_t>(geoNode)] = geoNode; } ++i; } context.getRenderingContext().popDepthBuffer(); } delete [] queries; } else {//mode == DIRECTION_VALUES const auto nodes = collectVisibleNodes(&node, context); if(doesCountPolygons()){ uint32_t numPolys=0; for(const auto & n : nodes) { numPolys += n->getTriangleCount(); } values->push_back(new Util::_NumberAttribute<float>(numPolys)); }else{ values->push_back(new Util::_NumberAttribute<float>(nodes.size())); } } }
void ColorVisibilityEvaluator::measure(FrameContext & context, Node & node, const Geometry::Rect & rect) { Rendering::RenderingContext & rCtxt = context.getRenderingContext(); // ### Set up textures for the framebuffer object. ### const uint32_t width = static_cast<uint32_t> (rect.getWidth()); const uint32_t height = static_cast<uint32_t> (rect.getHeight()); if (colorTexture == nullptr || width != static_cast<uint32_t> (colorTexture->getWidth()) || height != static_cast<uint32_t> (colorTexture->getHeight())) { // (Re-)Create textures with correct dimension. rCtxt.pushAndSetFBO(fbo.get()); fbo->detachColorTexture(context.getRenderingContext()); fbo->detachDepthTexture(context.getRenderingContext()); rCtxt.popFBO(); colorTexture = Rendering::TextureUtils::createStdTexture(width, height, true); depthTexture = Rendering::TextureUtils::createDepthTexture(width, height); // Bind textures to FBO. rCtxt.pushAndSetFBO(fbo.get()); fbo->attachColorTexture(context.getRenderingContext(),colorTexture.get()); fbo->attachDepthTexture(context.getRenderingContext(),depthTexture.get()); rCtxt.popFBO(); } // ### Render the scene into a framebuffer object. ### rCtxt.pushAndSetFBO(fbo.get()); rCtxt.pushAndSetShader(getShader()); rCtxt.clearScreen(Util::Color4f(0.0f, 0.0f, 0.0f, 0.0f)); // Color counter for assignment of unique colors to triangles. // Background is black (color 0). int32_t currentColor = 1; // Collect potentially visible geometry nodes. const auto geoNodes = collectNodesInFrustum<GeometryNode>(&node, context.getCamera()->getFrustum()); for(const auto & geoNode : geoNodes) { getShader()->setUniform(rCtxt, Rendering::Uniform("colorOffset", currentColor)); context.displayNode(geoNode, USE_WORLD_MATRIX | NO_STATES); currentColor += geoNode->getTriangleCount(); } rCtxt.popShader(); rCtxt.popFBO(); // ### Read texture and analyze colors. ### // Map from colors to number of occurrences. std::map<uint32_t, uint32_t> colorUsage; // Current color is compared with the previous color. If they match, the local count variable is used as cache before insertion into map is performed. uint32_t lastColor = 0; uint32_t lastCount = 0; colorTexture->downloadGLTexture(rCtxt); const uint32_t * texData = reinterpret_cast<const uint32_t *> (colorTexture->openLocalData(rCtxt)); const uint32_t numPixels = width * height; for (uint_fast32_t p = 0; p < numPixels; ++p) { const uint32_t & color = texData[p]; if (color == 0) { // Ignore background. continue; } if (color == lastColor) { ++lastCount; continue; } else { // Insert previous color. if (lastColor != 0) { increaseColorUsage(lastColor, lastCount, colorUsage); } lastColor = color; lastCount = 1; } } if (lastColor != 0) { increaseColorUsage(lastColor, lastCount, colorUsage); } if (mode == SINGLE_VALUE) { numTrianglesVisible += colorUsage.size(); } else { values->push_back(Util::GenericAttribute::createNumber(colorUsage.size())); } }
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; }