So I have a sorting problem. What I want to do is construct an octree around the camera position, copy the leaf node data to a temporary container, then reset the octree and enter a new camera position. After this, check if any of the new node positions are in the old leaf nodes, and only update the data in the positions that have changed. The result should be that the positions closest to the camera have a higher level of detail and a larger number of nodes, as the octree is subdivided based on how close the node's center is to the camera position.
The image below illustrates this simply. If the black boxes represent the octree leaf nodes from the old camera position, none of them would be included in the new set as their centers are not the same as any of the pink boxes.
Obviously, the result I'm getting with my code isn't what it should be. There are huge areas of terrain that don't load the new positions properly/overlap with existing terrain.
I'm using an unordered set for the leaf nodes, as each one's position should be unique, and an unordered map for the chunks I want to load, so that I can load the correct chunk based on the key which is the position of the leafnode.
In the code, I update the list of chunks to create only if the amount of nodes in the world changes: if (tempNodes.size() != _octree->leafNodes.size())
The rest of the logic pertains to what I've already been explaining. It might just be some simple mistake, but I also might just be misinterpreting the way certain member functions work.
void LandscapeManager::updateCenter(glm::ivec3 newCenter)
{
std::unordered_set<Octree*> tempNodes;
tempNodes = _octree->leafNodes;
// reconstruct octree
delete _octree;
_octree = new Octree(glm::vec3(0, 0, 0), glm::vec3(pow(2, 8), pow(2, 8), pow(2, 8)));
// update camera position in octree
_octree->leafNodes.clear();
_octree->insert(newCenter);
std::cout << "Generating: " << _octree->leafNodes.size() << " chunks...\n";
if (tempNodes.size() != _octree->leafNodes.size())
{
// clear chunk list and refresh with new list generated by code below
_chunkLoader.Clear();
for (auto i : _octree->leafNodes)
{
// add a chunk to the chunkloader if it's position does not exist in the old octree's leaf nodes
if (!tempNodes.count(i))
{
_chunkLoader.Add(i->getOrigin(), i->getHalfSize());
}
if (tempNodes.count(i))
{
// remove unchanged leaf nodes from the old list so that all
// the remains is a list of changed leaf nodes, which will be used to delete old chunks
tempNodes.erase(tempNodes.find(i));
}
}
for (auto i : tempNodes)
{
if (_chunks.count(i->getOrigin()))
{
// unload and delete the meshes that have changed
auto unload = _chunks.find(i->getOrigin());
_chunks.at(i->getOrigin())->Unload();
delete _chunks.at(i->getOrigin());
_chunks.erase(unload);
}
}
}
_center = newCenter;
}
Related
I have a function that draws triangles through OpenGL
I draw two triangles by pressing a button (function on_drawMapPushButton_clicked()).
Then i draw a sphere that placed above these triangles. And now i see, that sphere is drawed correctly over first triangle, but second triangle drawed over the sphere and not vice versa.
If i press the button second time, then spehere is drawed correctly over first and second triangles.
When i press the button third time, then second triangle drawed over the sphere again.
When i press the button fourth time, then spehere is drawed correctly over first and second triangles and so on.
If i use in sphereMesh QPhongMaterial instead of QPhongAlphaMaterial, then spehere is drawed correctly over first and second triangles always. Like it must to be.
I can't understand what i do wrong to get my sphere is drawed always over the triangles.
Code, that draws transparent sphere:
selectModel_ = new Qt3DExtras::QSphereMesh(selectEntity_);
selectModel_->setRadius(75);
selectModel_->setSlices(150);
selectMaterial_ = new Qt3DExtras::QPhongAlphaMaterial(selectEntity_);
selectMaterial_->setAmbient(QColor(28, 61, 136));
selectMaterial_->setDiffuse(QColor(11, 56, 159));
selectMaterial_->setSpecular(QColor(10, 67, 199));
selectMaterial_->setShininess(0.8f);
selectEntity_->addComponent(selectModel_);
selectEntity_->addComponent(selectMaterial_);
Function drawTriangles:
void drawTriangles(QPolygonF triangles, QColor color){
int numOfVertices = triangles.size();
// Create and fill vertex buffer
QByteArray bufferBytes;
bufferBytes.resize(3 * numOfVertices * static_cast<int>(sizeof(float)));
float *positions = reinterpret_cast<float*>(bufferBytes.data());
for(auto point : triangles){
*positions++ = static_cast<float>(point.x());
*positions++ = 0.0f; //We need to drow only on the surface
*positions++ = static_cast<float>(point.y());
}
geometry_ = new Qt3DRender::QGeometry(mapEntity_);
auto *buf = new Qt3DRender::QBuffer(geometry_);
buf->setData(bufferBytes);
positionAttribute_ = new Qt3DRender::QAttribute(mapEntity_);
positionAttribute_->setName(Qt3DRender::QAttribute::defaultPositionAttributeName());
positionAttribute_->setVertexBaseType(Qt3DRender::QAttribute::Float); //In our buffer we will have only floats
positionAttribute_->setVertexSize(3); // Size of a vertex
positionAttribute_->setAttributeType(Qt3DRender::QAttribute::VertexAttribute); // Attribute type
positionAttribute_->setByteStride(3 * sizeof(float));
positionAttribute_->setBuffer(buf);
geometry_->addAttribute(positionAttribute_); // Add attribute to ours Qt3DRender::QGeometry
// Create and fill an index buffer
QByteArray indexBytes;
indexBytes.resize(numOfVertices * static_cast<int>(sizeof(unsigned int))); // start to end
unsigned int *indices = reinterpret_cast<unsigned int*>(indexBytes.data());
for(unsigned int i = 0; i < static_cast<unsigned int>(numOfVertices); ++i) {
*indices++ = i;
}
auto *indexBuffer = new Qt3DRender::QBuffer(geometry_);
indexBuffer->setData(indexBytes);
indexAttribute_ = new Qt3DRender::QAttribute(geometry_);
indexAttribute_->setVertexBaseType(Qt3DRender::QAttribute::UnsignedInt); //In our buffer we will have only unsigned ints
indexAttribute_->setAttributeType(Qt3DRender::QAttribute::IndexAttribute); // Attribute type
indexAttribute_->setBuffer(indexBuffer);
indexAttribute_->setCount(static_cast<unsigned int>(numOfVertices)); // Set count of our vertices
geometry_->addAttribute(indexAttribute_); // Add the attribute to ours Qt3DRender::QGeometry
shape_ = new Qt3DRender::QGeometryRenderer(mapEntity_);
shape_->setPrimitiveType(Qt3DRender::QGeometryRenderer::Triangles);
shape_->setGeometry(geometry_);
//Create material
material_ = new Qt3DExtras::QPhongMaterial(mapEntity_);
material_->setAmbient(color);
trianglesEntity_ = new Qt3DCore::QEntity(mapEntity_);
trianglesEntity_->addComponent(shape_);
trianglesEntity_->addComponent(material_);
}
Press button handler on_drawMapPushButton_clicked():
void on_drawMapPushButton_clicked()
{
clearMap(); //Implementation is above
QPolygonF triangle1;
triangle1 << QPointF( 0 ,-1000) << QPointF(0 ,1000) << QPointF(1000, -1000);
drawTriangles(triangle1, Qt::black);
QPolygonF triangle2;
triangle2 << QPointF(-1000,-1000) << QPointF(-100,1000) << QPointF(-100,-1000);
drawTriangles(triangle2, Qt::red);
}
Map clearing function clearMap():
void clearMap()
{
if(mapEntity_){
delete mapEntity_;
mapEntity_ = nullptr;
mapEntity_ = new Qt3DCore::QEntity(view3dRootEntity_);
}
}
Ok here comes the extend answer.
The reason why this sometimes happens and sometimes not depends on the order of your entities. If you experiment with two simple spheres, one transparent and one not, you will see that when the sphere that is transparent is added later it will be drawn above the opaque object - just like you want it to.
This happens because the opaque object will be drawn first (it comes first in the scene graph) and the transparent object later which will give you the result you want. In the other case where the transparent object gets drawn first, the opaque object is drawn above because the QPhongAlphaMaterial has a QNoDepthMask render state which tells it not to write to the depth buffer. Thus, the opaque object always passes the depth test, where the transparent object actually already drew to. You have to do some more work to properly draw transparent objects for arbitrary scene graphs and camera positions.
The Qt3D Rendering Graph
To understand what you have to do you should understand how the Qt3D rendering graph is laid out. If you know this already you can skip this part.
Italic words reference items in the graph image in the following text.
If you use a Qt3DWindow, you can't access the root node of rendering graph. It is maintained by the window. You can access the QRenderSettings and root node of your framegraph through the functions activeFramegraph() and renderSettings() which you can both call on the window. You can also set the root node of scene graph through the setRootEntity() function of Qt3DWindow. The window internally has a QAspectEngine, where it sets the root node of the whole graph, which is the root node of the rendering graph in the graph image above.
If you want to insert a framegraph node to the existing framegraph of the 3D window, you have to add it as the parent of the active framegraph which I will explain in the next section. If you have your own custom framegraph which you set on the window through setActiveFramegraph() then just append it to the end, this should suffice.
Using QSortPolicy
As you already found out according to you other questions, you can use QSortPolicy in your framegraph to sort the entities by distance to camera. You can add a sort policy as follows (assuming that view is your Qt3DWindow and scene is your root entity of the scene graph, although I don't understand why it has to be):
Qt3DRender::QFrameGraphNode *framegraph = view.activeFrameGraph();
Qt3DRender::QSortPolicy *sortPolicy = new Qt3DRender::QSortPolicy(scene);
framegraph->setParent(sortPolicy);
QVector<Qt3DRender::QSortPolicy::SortType> sortTypes =
QVector<Qt3DRender::QSortPolicy::SortType>() << Qt3DRender::QSortPolicy::BackToFront;
sortPolicy->setSortTypes(sortTypes);
view.setActiveFrameGraph(framegraph);
The issue with this code is that this sort policy sorts the entities by the distance of their centers to the camera. If one of the opaque objects is closer to the camera than the transparent object it gets drawn later anyways and occludes the transparent object. See the images below for a graphical explanation.
The red and black sphere are further away from the camera than the torus, that's why they get drawn first and they don't occlude the torus.
No the center of the red sphere is closer to the camera than the center of the torus. It gets rendered later than the torus and occludes it.
Using Two Framegraph Branches
You can tackle the issue above if you use two framegraph branches. One which draws all opaque entities and one which draws all transparent ones. To achieve this you have to make use of QLayer and QLayerFilter. You can attach layers to entities and then add layer filters to your framegraph. This way you can exclude entities from entering a certain branch of your framegraph.
Let's say you create two layers, one for opaque objects and one for transparents ones:
Qt3DRender::QLayer *transparentLayer = new Qt3DRender::QLayer;
Qt3DRender::QLayer *opaqueLayer = new Qt3DRender::QLayer;
You have to attach the transparent layer to each transparent object and the opaque layer to each opaque object as a component (using addComponent()).
Unfortunately, you need a special framegraph tree to include the two corresponding layer filters (again, assuming that view is your Qt3DWindow):
Qt3DRender::QRenderSurfaceSelector *renderSurfaceSelector
= new Qt3DRender::QRenderSurfaceSelector();
renderSurfaceSelector->setSurface(&view);
Qt3DRender::QClearBuffers *clearBuffers
= new Qt3DRender::QClearBuffers(renderSurfaceSelector);
clearBuffers->setBuffers(Qt3DRender::QClearBuffers::AllBuffers);
clearBuffers->setClearColor(Qt::white);
This is the first branch to clear the buffers. Now you add the following code:
Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport(renderSurfaceSelector);
Qt3DRender::QCameraSelector *cameraSelector = new Qt3DRender::QCameraSelector(viewport);
Qt3DRender::QCamera *camera = new Qt3DRender::QCamera(cameraSelector);
// set your camera parameters here
cameraSelector->setCamera(camera);
Since you create the QViewport as a child of the QRenderSurfaceSelector it is now a sibling in your framegraph with respect to the QClearBuffers. You can see an illustration of the example framegraphs here.
Now you have to create the two leaf nodes that contain the layer filters. The Qt3D engine always executes a whole branch when it reaches a leaf. This means that first the opaque objects are drawn and then the transparent ones.
// not entirely sure why transparent filter has to go first
// I would have expected the reversed order of the filters but this works...
Qt3DRender::QLayerFilter *transparentFilter = new Qt3DRender::QLayerFilter(camera);
transparentFilter->addLayer(transparentLayer);
Qt3DRender::QLayerFilter *opaqueFilter = new Qt3DRender::QLayerFilter(camera);
opaqueFilter->addLayer(opaqueLayer);
The two layer filters are now leaf nodes in your framegraph branch and Qt3D will first draw the opaque objects and then afterwards, since it uses the same viewport and everything, will draw the transparent objects above. It will draw them correctly (i.e. not in front of parts of opaque objects that the transparent object actually lies behind, because we did not clear the depth buffers again -> Splitting the framegraph happens only on the camera node).
Now set the new framegaph on your Qt3DWindow:
view.setActiveFrameGraph(renderSurfaceSelector);
Result:
Edit (26.03.21): As Patrick B. pointed out correctly, using the suggested solution with two layers you will have to add both layers as components to any lights in the scene. You can get around this by setting the filter mode on the QLayerFilters to QLayerFilter::FilterMode::DiscardAnyMatching and then reverse the order of the filters. This way, the transparentFilter discards any entities with the transparentLayer attached - but not the lights because they don't have the transparentLayer. Vice versa for the opaqueFilter.
My mistake was that i did wrong order of creating and deletion of Triangles and Sphere entities.
In pseudo code right order is as follows:
clearTriangles();
clearSphere();
drawTriangles();
drawSphere();
If you are using Qt3d with QML and want to control the order elements are drawn you can control it by the order of layers in your QML file.
Something like:
{
objectName: "firstLayer"
id : firstLayer
}
Layer {
objectName: "secondLayer"
id: secondLayer
}
The order you add them to layer filters will then control which is drawn first:
RenderSurfaceSelector {
CameraSelector {
id : cameraSelector
camera: mainCamera
FrustumCulling {
ClearBuffers {
buffers : ClearBuffers.AllBuffers
clearColor: "#04151c"
NoDraw {}
}
LayerFilter
{
objectName: "firstLayerFilter"
id: firstLayerFilter
layers: [firstLayer]
}
LayerFilter
{
id: secondLayerFilter
objectName: "secondLayerFilter"
layers: [secondLayer]
}
Then anything you add to the secondLayer will get drawn over-top of the first layer. I used this to make sure text always showed up in front of shapes, but it can be used similarly with transparencies.
I have a function that draws triangles through OpenGL
I draw two triangles by pressing a button (function on_drawMapPushButton_clicked()).
Then i draw a sphere that placed above these triangles. And now i see, that sphere is drawed correctly over first triangle, but second triangle drawed over the sphere and not vice versa.
If i press the button second time, then spehere is drawed correctly over first and second triangles.
When i press the button third time, then second triangle drawed over the sphere again.
When i press the button fourth time, then spehere is drawed correctly over first and second triangles and so on.
If i use in sphereMesh QPhongMaterial instead of QPhongAlphaMaterial, then spehere is drawed correctly over first and second triangles always. Like it must to be.
I can't understand what i do wrong to get my sphere is drawed always over the triangles.
Code, that draws transparent sphere:
selectModel_ = new Qt3DExtras::QSphereMesh(selectEntity_);
selectModel_->setRadius(75);
selectModel_->setSlices(150);
selectMaterial_ = new Qt3DExtras::QPhongAlphaMaterial(selectEntity_);
selectMaterial_->setAmbient(QColor(28, 61, 136));
selectMaterial_->setDiffuse(QColor(11, 56, 159));
selectMaterial_->setSpecular(QColor(10, 67, 199));
selectMaterial_->setShininess(0.8f);
selectEntity_->addComponent(selectModel_);
selectEntity_->addComponent(selectMaterial_);
Function drawTriangles:
void drawTriangles(QPolygonF triangles, QColor color){
int numOfVertices = triangles.size();
// Create and fill vertex buffer
QByteArray bufferBytes;
bufferBytes.resize(3 * numOfVertices * static_cast<int>(sizeof(float)));
float *positions = reinterpret_cast<float*>(bufferBytes.data());
for(auto point : triangles){
*positions++ = static_cast<float>(point.x());
*positions++ = 0.0f; //We need to drow only on the surface
*positions++ = static_cast<float>(point.y());
}
geometry_ = new Qt3DRender::QGeometry(mapEntity_);
auto *buf = new Qt3DRender::QBuffer(geometry_);
buf->setData(bufferBytes);
positionAttribute_ = new Qt3DRender::QAttribute(mapEntity_);
positionAttribute_->setName(Qt3DRender::QAttribute::defaultPositionAttributeName());
positionAttribute_->setVertexBaseType(Qt3DRender::QAttribute::Float); //In our buffer we will have only floats
positionAttribute_->setVertexSize(3); // Size of a vertex
positionAttribute_->setAttributeType(Qt3DRender::QAttribute::VertexAttribute); // Attribute type
positionAttribute_->setByteStride(3 * sizeof(float));
positionAttribute_->setBuffer(buf);
geometry_->addAttribute(positionAttribute_); // Add attribute to ours Qt3DRender::QGeometry
// Create and fill an index buffer
QByteArray indexBytes;
indexBytes.resize(numOfVertices * static_cast<int>(sizeof(unsigned int))); // start to end
unsigned int *indices = reinterpret_cast<unsigned int*>(indexBytes.data());
for(unsigned int i = 0; i < static_cast<unsigned int>(numOfVertices); ++i) {
*indices++ = i;
}
auto *indexBuffer = new Qt3DRender::QBuffer(geometry_);
indexBuffer->setData(indexBytes);
indexAttribute_ = new Qt3DRender::QAttribute(geometry_);
indexAttribute_->setVertexBaseType(Qt3DRender::QAttribute::UnsignedInt); //In our buffer we will have only unsigned ints
indexAttribute_->setAttributeType(Qt3DRender::QAttribute::IndexAttribute); // Attribute type
indexAttribute_->setBuffer(indexBuffer);
indexAttribute_->setCount(static_cast<unsigned int>(numOfVertices)); // Set count of our vertices
geometry_->addAttribute(indexAttribute_); // Add the attribute to ours Qt3DRender::QGeometry
shape_ = new Qt3DRender::QGeometryRenderer(mapEntity_);
shape_->setPrimitiveType(Qt3DRender::QGeometryRenderer::Triangles);
shape_->setGeometry(geometry_);
//Create material
material_ = new Qt3DExtras::QPhongMaterial(mapEntity_);
material_->setAmbient(color);
trianglesEntity_ = new Qt3DCore::QEntity(mapEntity_);
trianglesEntity_->addComponent(shape_);
trianglesEntity_->addComponent(material_);
}
Press button handler on_drawMapPushButton_clicked():
void on_drawMapPushButton_clicked()
{
clearMap(); //Implementation is above
QPolygonF triangle1;
triangle1 << QPointF( 0 ,-1000) << QPointF(0 ,1000) << QPointF(1000, -1000);
drawTriangles(triangle1, Qt::black);
QPolygonF triangle2;
triangle2 << QPointF(-1000,-1000) << QPointF(-100,1000) << QPointF(-100,-1000);
drawTriangles(triangle2, Qt::red);
}
Map clearing function clearMap():
void clearMap()
{
if(mapEntity_){
delete mapEntity_;
mapEntity_ = nullptr;
mapEntity_ = new Qt3DCore::QEntity(view3dRootEntity_);
}
}
Ok here comes the extend answer.
The reason why this sometimes happens and sometimes not depends on the order of your entities. If you experiment with two simple spheres, one transparent and one not, you will see that when the sphere that is transparent is added later it will be drawn above the opaque object - just like you want it to.
This happens because the opaque object will be drawn first (it comes first in the scene graph) and the transparent object later which will give you the result you want. In the other case where the transparent object gets drawn first, the opaque object is drawn above because the QPhongAlphaMaterial has a QNoDepthMask render state which tells it not to write to the depth buffer. Thus, the opaque object always passes the depth test, where the transparent object actually already drew to. You have to do some more work to properly draw transparent objects for arbitrary scene graphs and camera positions.
The Qt3D Rendering Graph
To understand what you have to do you should understand how the Qt3D rendering graph is laid out. If you know this already you can skip this part.
Italic words reference items in the graph image in the following text.
If you use a Qt3DWindow, you can't access the root node of rendering graph. It is maintained by the window. You can access the QRenderSettings and root node of your framegraph through the functions activeFramegraph() and renderSettings() which you can both call on the window. You can also set the root node of scene graph through the setRootEntity() function of Qt3DWindow. The window internally has a QAspectEngine, where it sets the root node of the whole graph, which is the root node of the rendering graph in the graph image above.
If you want to insert a framegraph node to the existing framegraph of the 3D window, you have to add it as the parent of the active framegraph which I will explain in the next section. If you have your own custom framegraph which you set on the window through setActiveFramegraph() then just append it to the end, this should suffice.
Using QSortPolicy
As you already found out according to you other questions, you can use QSortPolicy in your framegraph to sort the entities by distance to camera. You can add a sort policy as follows (assuming that view is your Qt3DWindow and scene is your root entity of the scene graph, although I don't understand why it has to be):
Qt3DRender::QFrameGraphNode *framegraph = view.activeFrameGraph();
Qt3DRender::QSortPolicy *sortPolicy = new Qt3DRender::QSortPolicy(scene);
framegraph->setParent(sortPolicy);
QVector<Qt3DRender::QSortPolicy::SortType> sortTypes =
QVector<Qt3DRender::QSortPolicy::SortType>() << Qt3DRender::QSortPolicy::BackToFront;
sortPolicy->setSortTypes(sortTypes);
view.setActiveFrameGraph(framegraph);
The issue with this code is that this sort policy sorts the entities by the distance of their centers to the camera. If one of the opaque objects is closer to the camera than the transparent object it gets drawn later anyways and occludes the transparent object. See the images below for a graphical explanation.
The red and black sphere are further away from the camera than the torus, that's why they get drawn first and they don't occlude the torus.
No the center of the red sphere is closer to the camera than the center of the torus. It gets rendered later than the torus and occludes it.
Using Two Framegraph Branches
You can tackle the issue above if you use two framegraph branches. One which draws all opaque entities and one which draws all transparent ones. To achieve this you have to make use of QLayer and QLayerFilter. You can attach layers to entities and then add layer filters to your framegraph. This way you can exclude entities from entering a certain branch of your framegraph.
Let's say you create two layers, one for opaque objects and one for transparents ones:
Qt3DRender::QLayer *transparentLayer = new Qt3DRender::QLayer;
Qt3DRender::QLayer *opaqueLayer = new Qt3DRender::QLayer;
You have to attach the transparent layer to each transparent object and the opaque layer to each opaque object as a component (using addComponent()).
Unfortunately, you need a special framegraph tree to include the two corresponding layer filters (again, assuming that view is your Qt3DWindow):
Qt3DRender::QRenderSurfaceSelector *renderSurfaceSelector
= new Qt3DRender::QRenderSurfaceSelector();
renderSurfaceSelector->setSurface(&view);
Qt3DRender::QClearBuffers *clearBuffers
= new Qt3DRender::QClearBuffers(renderSurfaceSelector);
clearBuffers->setBuffers(Qt3DRender::QClearBuffers::AllBuffers);
clearBuffers->setClearColor(Qt::white);
This is the first branch to clear the buffers. Now you add the following code:
Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport(renderSurfaceSelector);
Qt3DRender::QCameraSelector *cameraSelector = new Qt3DRender::QCameraSelector(viewport);
Qt3DRender::QCamera *camera = new Qt3DRender::QCamera(cameraSelector);
// set your camera parameters here
cameraSelector->setCamera(camera);
Since you create the QViewport as a child of the QRenderSurfaceSelector it is now a sibling in your framegraph with respect to the QClearBuffers. You can see an illustration of the example framegraphs here.
Now you have to create the two leaf nodes that contain the layer filters. The Qt3D engine always executes a whole branch when it reaches a leaf. This means that first the opaque objects are drawn and then the transparent ones.
// not entirely sure why transparent filter has to go first
// I would have expected the reversed order of the filters but this works...
Qt3DRender::QLayerFilter *transparentFilter = new Qt3DRender::QLayerFilter(camera);
transparentFilter->addLayer(transparentLayer);
Qt3DRender::QLayerFilter *opaqueFilter = new Qt3DRender::QLayerFilter(camera);
opaqueFilter->addLayer(opaqueLayer);
The two layer filters are now leaf nodes in your framegraph branch and Qt3D will first draw the opaque objects and then afterwards, since it uses the same viewport and everything, will draw the transparent objects above. It will draw them correctly (i.e. not in front of parts of opaque objects that the transparent object actually lies behind, because we did not clear the depth buffers again -> Splitting the framegraph happens only on the camera node).
Now set the new framegaph on your Qt3DWindow:
view.setActiveFrameGraph(renderSurfaceSelector);
Result:
Edit (26.03.21): As Patrick B. pointed out correctly, using the suggested solution with two layers you will have to add both layers as components to any lights in the scene. You can get around this by setting the filter mode on the QLayerFilters to QLayerFilter::FilterMode::DiscardAnyMatching and then reverse the order of the filters. This way, the transparentFilter discards any entities with the transparentLayer attached - but not the lights because they don't have the transparentLayer. Vice versa for the opaqueFilter.
My mistake was that i did wrong order of creating and deletion of Triangles and Sphere entities.
In pseudo code right order is as follows:
clearTriangles();
clearSphere();
drawTriangles();
drawSphere();
If you are using Qt3d with QML and want to control the order elements are drawn you can control it by the order of layers in your QML file.
Something like:
{
objectName: "firstLayer"
id : firstLayer
}
Layer {
objectName: "secondLayer"
id: secondLayer
}
The order you add them to layer filters will then control which is drawn first:
RenderSurfaceSelector {
CameraSelector {
id : cameraSelector
camera: mainCamera
FrustumCulling {
ClearBuffers {
buffers : ClearBuffers.AllBuffers
clearColor: "#04151c"
NoDraw {}
}
LayerFilter
{
objectName: "firstLayerFilter"
id: firstLayerFilter
layers: [firstLayer]
}
LayerFilter
{
id: secondLayerFilter
objectName: "secondLayerFilter"
layers: [secondLayer]
}
Then anything you add to the secondLayer will get drawn over-top of the first layer. I used this to make sure text always showed up in front of shapes, but it can be used similarly with transparencies.
Let me first state that I just want direction, not necessarily actual code, unless a small snippet is the only way to get the point across.
I need to create a DIRECTED graph data structure using an adjacency list or matrix in C++, and add vertices/edges from standard input, which means dynamically.
I think I'd be able to create a graph fine if I was able to instantiate a set of Vertices first, then create edges and add them to the graph, but I don't understand how it is possible to add an edge which contains a vertex that hasn't been instantiated yet.
for example, the first line from standard input reads:
Miami -> New York/1100 -> Washington/1000 -> albuquerque/1700
How am I supposed to add an edge from Miami to New York if the New York vertex hasn't been added to the graph yet?
Thanks for the direction everyone!
how it is possible to add an edge which
contains a vertex that hasn't been instantiated yet.
Simple: instantiate it..
I do not see any issue with this. Assume V to be the vertex set seen so far. V is initially empty. As you read the input x->y, you get its end points (x and y). If any one of them is not instantiated (i.e., not in V), you instantiate it and add it to the vertex set.
Another way to look to it: imagine we are defining the graph by its edge set E. By definition any edge is a pair of vertices which in turn defines the vertex set of the graph.
How about you resize the adjacency list each time a new unique node comes in? You can maintain a set of unique node values and use its size to adjust the size of the adjacency list each time you have to add a node. Below is a some code that does the same.
class Graph
{
public:
// Add links in the graph
void addLink(int id1, int id2){
// Add to hashset
uniqueNodes.insert(id1);
uniqueNodes.insert(id2);
// Resize on the adjacency list based on how many nodes exists in the uniqueNodes set
adjList.resize(uniqueNodes.size());
// Make the connections assuming undirected graph
adjList[id1].push_back(id2);
adjList[id2].push_back(id1);
}
// Print the graph
void printGraph(){
for(int i = 0; i < adjList.size(); i++){
cout << i << ":";
for(auto it = adjList[i].begin(); it != adjList[i].end(); it++)
cout << *it << "->";
cout << "NULL\n";
}
}
private:
// Adjacency list for the graph
vector<list<int>> adjList;
// Hashset to help define the size of the adjacency list as nodes come in
set<int> uniqueNodes;
};
I would like to get the VID (vertex ID) after I have added a single vertex to an existing graph. I current get a vertex_set after adding the new vertex and loop through to the end of the vertex set (assuming this is always the last added vertex even in the event of a earlier one being deleted?). I need to test if deleting a vertex from the middle of the set changes the VIDs still. But I am sure there must be a better (read more efficient way) of doing this.. The code below is what I currently use.
Any help appreciated as I am new to iGraph.
// add into graph
igraph_integer_t t = 1;
if(igraph_add_vertices(user_graph, t, 0) != 0)
{
::MessageBoxW(NULL, L"Failed to add vertex to iGraph, vertex not added.", L"Network Model", MB_ICONSTOP);
return false;
}
/* get all verticies */
igraph_vs_t vertex_set;
igraph_vit_t vit;
igraph_integer_t vid = 0;
igraph_vs_all(&vertex_set);
igraph_vit_create(user_graph, vertex_set, &vit);
// must be a better way - look for starting from end.
while (!IGRAPH_VIT_END(vit))
{
vid = IGRAPH_VIT_GET(vit);
IGRAPH_VIT_NEXT(vit);
}
// add vid to vertex ca
ca->graphid = (int)vid;
// Add new vertex to local store
vm->CreateVertex(ca);
// cleanup
igraph_vit_destroy(&vit);
igraph_vs_destroy(&vertex_set);
Vertex IDs (and also edge IDs) in igraph are integers from zero up to the number of vertices/edges minus one. Therefore, if you add a new vertex or edge, its ID will always be equal to the number of vertices/edges before the addition. Also, if you delete some edges, the IDs of existing edges will be re-arranged to make the edge ID range continuous again. Same applies for the deletion of vertices, and note that deleting some vertices will also re-arrange the edge IDs unless the deleted vertices were isolated.
I've been trying to implement a three dimensional BSP tree to render single objects (a cube, a box with a cylinder in it, etc.) which are transparent. As far as I understand, this should work, but it is not, and I can't figure out why. Everything I've read refers to BSP tree being used in either two dimensions or on multiple objects, so I was wondering if I've just generally misunderstood what BSP trees can be applied to rather than had an error in my code. I've looked at a lot of things online, and my code seems to be the same as Bretton Wade's (ftp://ftp.sgi.com/other/bspfaq/faq/bspfaq.html), so if anybody has any samples of BSP code for single objects/transparency in particular, that would be wonderful.
Thank you.
BSP trees can be abstracted to any N-dimensional space since by definition a N-dimensional hyperplane will bisect a space into two parts. In other words, for a given point in N-dimensional space, it must either be on the hyperplane, or in one of the bisected spaces that the hyperplane creates in the N-dimensional space.
For 2D, a BSP tree would be created by drawing a line, and then testing on what side of that line a point was. This is because a line bisects a 2D-space. For 3D, you would need a plane, which would typically be formed from the normal to the polygonal surface that you're using as the test.
So your algorithm would be something like the following:
Create a queue containing all the polys from the cube. It would be best to randomly add the polys to the queue rather than in some order.
Pop a poly from the front of the queue ... make this the "root" of the BSP tree.
Create a normal from that poly
Pop another poly from the queue
Test whether all the points in that poly are in front of or behind the normal created from the root.
If they are all in-front, then make that poly the right-child of the root. If they are all behind, make that poly the left-child of the root.
If all the points in the poly are not in front or behind the plane defined by the root polygon's normal, then you'll need to split the poly into two parts for the portions that are in-front and behind the plane. For the two new polys created from this split, add them to the back of the queue, and repeat from step #4.
Pop another poly from the queue.
Test this poly against the root. Since the root has a child, once you test whether the poly is in-front or behind the root (keeping in mind step #7 that may require a split), test the poly against the child node that is on the right if it's in-front, or the child node on the left if it's behind. If there is no child-node, then you can stop moving through the tree, and add the polygon as to the tree as that child.
For any child node you run into where the current poly is not in-front or behind, you'll need to perform the split in step #7 and then go back to step #4.
Keep repeating this process until the queue is empty.
Code for this algorithm would conceptually look something like:
struct bsp_node
{
std::vector<poly_t> polys;
bsp_node* rchild;
bsp_node* lchild;
bsp_node(const poly_t& input): rchild(NULL), lchild(NULL)
{
polys.push_back(input);
}
};
std::queue<poly_t> poly_queue;
//...add all the polygons in the scene randomly to the queue
bsp_node* bsp_root = new bsp_node(poly_queue.front());
poly_queue.pop();
while(!poly_queue.empty())
{
//grab a poly from the queue
poly_t current_poly = poly_queue.front();
poly_queue.pop();
//search the binary tree
bsp_node* current_node = bsp_root;
bsp_node* prev_node = NULL;
bool stop_search = false;
while(current_node != NULL && !stop_search)
{
//use a plane defined by the current_node to test current_poly
int result = test(current_poly, current_node);
switch(result)
{
case COINCIDENT:
stop_search = true;
current_node->polys.push_back(current_poly);
break;
case IN_FRONT:
prev_node = current_node;
current_node = current_node->rchild;
break;
case BEHIND:
prev_node = current_node;
current_node = current_node->lchild;
break;
//split the poly and add the newly created polygons back to the queue
case SPLIT:
stop_search = true;
split_current_poly(current_poly, poly_queue);
break;
}
}
//if we reached a NULL child, that means we can add the poly to the tree
if (!current_node)
{
if (prev_node->rchild == NULL)
prev_node->rchild = new bsp_node(current_poly);
else
prev_node->lchild = new bsp_node(current_poly);
}
}
Once you've completed the creation of the tree, you can then do an in-order search of the tree and get the polygons sorted from back-to-front. It won't matter if the objects are transparent or not, since you're sorting based on the polys themselves, not their material properties.