How to draw 3D lines onto a QML scene? - c++

I try to integrate Bullet Physics' debug drawing interface into QML, so I have to implement a drawLine() method.
void drawLine(const btVector3 &from, const btVector3 &to, const btVector3 &color);
What I tried is that I inherited an item, that is used in the scene, from both QQuickItem3D and btIDebugDraw. In drawLine(), I add the lines to a member vector. In Qt's drawItem(), I iterate over the lines and use OpenGL calls to render them. However, they do not appear on screen.
How can I draw the lines in 3D space and from the correct camera view?
void DebugDrawer::drawItem(QGLPainter *painter)
{
if (lines_.size() < 1)
return;
// Draw current lines
painter->modelViewMatrix().push();
glBegin(GL_LINES);
for (auto &line : lines_) {
glColor3f(line.color.getX(), line.color.getY(), line.color.getZ());
glVertex3f(line.from.getX(), line.from.getY(), line.from.getZ());
glVertex3f(line.to.getX(), line.to.getY(), line.to.getZ());
}
glEnd();
painter->modelViewMatrix().pop();
// Reset buffer
lines_.clear();
}

I ended up using QtQuick's line class and set its vertices using setVertices() in Bullet's flushLines() method.

Related

wxPaintDC - Shapes are drawn below current one and not over the current one

I am trying to implement a program that draws basic shapes. All is going well except that new Shapes are being drawn below the current shape. My Paint function in the frames sourcecode file looks like this:
void ShapeFrame::OnPaint(wxPaintEvent& event)
{
wxPaintDC dc(this);
wxGraphicsContext *gc = wxGraphicsContext::Create(dc);
wxGraphicsMatrix trans = gc->CreateMatrix();
setTransform(&trans);
ShapeData *data = wxGetApp().getData();
for(int i=0; i<data->getNumShapes(); i++)
{
data->getShape(i)->draw(dc, &trans);
}
delete gc;
}
A snapshot of the app screen is also given.
I am a beginner in wxWidgets the circle was drawn first and then the rectangle

GamePlay3d engine won't show model imported from fbx

I am a newbie with gameplay3d and went through all tutorials, however I cant manage to display this simple(not much polygons and material) model that I encoded from Fbx. I checked the model with unity3D, and a closed source software that uses gameplay3d and all seems to fine. I guess I am missing some detail loading the scene.
This is the model file including also the original fbx file. I suspect if it has something to do with light
https://www.dropbox.com/sh/ohgpsfnkm3iv24s/AACApRcxwtbmpKu4_5nnp8rZa?dl=0
This is the class that loads the scene.
#include "Demo.h"
// Declare our game instance
Demo game;
Demo::Demo()
: _scene(NULL), _wireframe(false)
{
}
void Demo::initialize()
{
// Load game scene from file
Bundle* bundle = Bundle::create("KGN56AI30N.gpb");
_scene = bundle->loadScene();
SAFE_RELEASE(bundle);
// Get the box model and initialize its material parameter values and bindings
Camera* camera = Camera::createPerspective(45.0f, getAspectRatio(), 1.0f, 20.0f);
Node* cameraNode = _scene->addNode("camera");
// Attach the camera to a node. This determines the position of the camera.
cameraNode->setCamera(camera);
// Make this the active camera of the scene.
_scene->setActiveCamera(camera);
SAFE_RELEASE(camera);
// Move the camera to look at the origin.
cameraNode->translate(0,0, 10);
cameraNode->rotateX(MATH_DEG_TO_RAD(0.25f));
// Update the aspect ratio for our scene's camera to match the current device resolution
_scene->getActiveCamera()->setAspectRatio(getAspectRatio());
// Set the aspect ratio for the scene's camera to match the current resolution
_scene->getActiveCamera()->setAspectRatio(getAspectRatio());
Light* directionalLight = Light::createDirectional(Vector3::one());
_directionalLightNode = Node::create("directionalLight");
_directionalLightNode->setLight(directionalLight);
SAFE_RELEASE(directionalLight);
_scene->addNode(_directionalLightNode);
_scene->setAmbientColor(1.0, 1.0, 1.0);
_scene->visit(this, &Demo::initializeMaterials);
}
bool Demo::initializeMaterials(Node* node)
{
Model* model = dynamic_cast<Model*>(node->getDrawable());
if (model)
{
for(int i=0;i<model->getMeshPartCount();i++)
{
Material* material = model->getMaterial(i);
if(material)
{
// For this sample we will only bind a single light to each object in the scene.
MaterialParameter* colorParam = material->getParameter("u_directionalLightColor[0]");
colorParam->setValue(Vector3(0.75f, 0.75f, 0.75f));
MaterialParameter* directionParam = material->getParameter("u_directionalLightDirection[0]");
directionParam->setValue(Vector3(1, 1, 1));
}
}
}
return true;
}
void Demo::finalize()
{
SAFE_RELEASE(_scene);
}
void Demo::update(float elapsedTime)
{
// Rotate model
//_scene->findNode("box")->rotateY(MATH_DEG_TO_RAD((float)elapsedTime / 1000.0f * 180.0f));
}
void Demo::render(float elapsedTime)
{
// Clear the color and depth buffers
clear(CLEAR_COLOR_DEPTH, Vector4::zero(), 1.0f, 0);
// Visit all the nodes in the scene for drawing
_scene->visit(this, &Demo::drawScene);
}
bool Demo::drawScene(Node* node)
{
// If the node visited contains a drawable object, draw it
Drawable* drawable = node->getDrawable();
if (drawable)
drawable->draw(_wireframe);
return true;
}
void Demo::keyEvent(Keyboard::KeyEvent evt, int key)
{
if (evt == Keyboard::KEY_PRESS)
{
switch (key)
{
case Keyboard::KEY_ESCAPE:
exit();
break;
}
}
}
void Demo::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
{
switch (evt)
{
case Touch::TOUCH_PRESS:
_wireframe = !_wireframe;
break;
case Touch::TOUCH_RELEASE:
break;
case Touch::TOUCH_MOVE:
break;
};
}
I can't download your dropbox .fbx file. How many models do you have in the scene? Here's a simple way of doing what you want to do -- not optimal, but it'll get you started...
So first off, I can't see where in your code you actually assign a Shader to be used with the material. I use something like this:
material = model->setMaterial("Shaders/Animation/ADSVertexViewAnim.vsh", "Shaders/Animation/ADSVertexViewAnim.fsh");
You need to assign a Shader, and the above code will take the vertex and fragment shaders and use that when the object needs to be drawn.
I went about it a slightly different way by not loading the scene file automatically, but creating an empty scene and then extracting my model from the bundle and adding it to the scene manually. That way, I can see exactly what is happening and I'm in control of each step. GamePlay3D has some fancy property files, but use them only once you know how the process works manually..
Initially, I created a simple cube in a scene, and created a scene manually, and added the monkey to the node graph, as follows:
void GameMain::ExtractFromBundle()
{
/// Create a new empty scene.
_scene = Scene::create();
// Create the Model and its Node
Bundle* bundle = Bundle::create("res/monkey.gpb"); // Create the bundle from GPB file
/// Create the Cube
{
Mesh* meshMonkey = bundle->loadMesh("Character_Mesh"); // Load the mesh from the bundle
Model* modelMonkey = Model::create(meshMonkey);
Node* nodeMonkey = _scene->addNode("Monkey");
nodeMonkey->setTranslation(0,0,0);
nodeMonkey->setDrawable(modelMonkey);
}
}
Then I want to search the scene graph and only assign a material to the object that I want to draw (the monkey). Use this if you want to assign different materials to different objects manually...
bool GameMain::initializeScene(Node* node)
{
Material* material;
std::cout << node->getId() << std::endl;
// find the node in the scene
if (strcmp(node->getId(), "Monkey") != 0)
return false;
Model* model = dynamic_cast<Model*>(node->getDrawable());
if( !model )
return false;
material = model->setMaterial("Shaders/Animation/ADSVertexViewAnim.vsh", "Shaders/Animation/ADSVertexViewAnim.fsh");
material->getStateBlock()->setCullFace(true);
material->getStateBlock()->setDepthTest(true);
material->getStateBlock()->setDepthWrite(true);
// The World-View-Projection Matrix is needed to be able to see view the 3D world thru the camera
material->setParameterAutoBinding("u_worldViewProjectionMatrix", "WORLD_VIEW_PROJECTION_MATRIX");
// This matrix is necessary to calculate normals properly, but the WORLD_MATRIX would also work
material->setParameterAutoBinding("u_worldViewMatrix", "WORLD_VIEW_MATRIX");
material->setParameterAutoBinding("u_viewMatrix", "VIEW_MATRIX");
return true;
}
Now the object is ready to be drawn.... so I use these functions:
void GameMain::render(float elapsedTime)
{
// Clear the color and depth buffers
clear(CLEAR_COLOR_DEPTH, Vector4(0.0, 0.0, 0.0, 0.0), 1.0f, 0);
// Visit all the nodes in the scene for drawing
_scene->visit(this, &GameMain::drawScene);
}
bool GameMain::drawScene(Node* node)
{
// If the node visited contains a drawable object, draw it
Drawable* drawable = node->getDrawable();
if (drawable)
drawable->draw(_wireframe);
return true;
}
I use my own shaders, so I don't have to worry about Light and DirectionalLight and all that stuff. Once I can see the object, then I'll add dynamic lights, etc, but for starters, start simple.
Regards.

Proper data model for a 2D Tilemap (C++, Qt)

I made a small 2D level editor where you can create 2D tile based maps..however, the performance inside my application is really really bad. I am currently thinking to start all over again.
The Problem is, I currently use QGraphicsItem's to represent a single tile inside a QGraphicsScene. A tile has some properties..including an image. When a map is created, I create an item for each tile which draws an image for each tile..which basically is a lot of graphicitems and it slows down the whole application. This is the function that populates a map once it is created :
for(int i=0;i<map->m_rows;i++)
{
for(int j=0;j<map->m_cols;j++)
{
Tile* thetile=map->getAt(i,j);
if(thetile)
{
if(map->getType()==twoditor::RECTANGLETILE)
{
QGraphicsItem* item= new TileGraphicsItem(thetile);
m_scene->addItem(item);
}
else if(map->getType()==twoditor::HEXAGONTILE)
{
QGraphicsItem* item= new HexagonGraphicsItem(thetile);
m_scene->addItem(item);
}
}
}
}
This works for a map with 100x100 Tiles. But if i want to create even larger maps..the loading time is really unbearable..
Can someone give me advice for a better representation of a tile map? Are there other convenient ways to show a map and edit cells(tiles) inside it?
EDIT: TileGraphicItem paint function:
void TileGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget){
setZValue(0);
if(!m_thetile->getImage().isNull())
{
painter->drawImage(0,0,m_thetile->getImage());
}
QPainterPath circle_path;
QRect duwagrect(boundingRect().x(),boundingRect().y(),boundingRect().width(),boundingRect().height());
circle_path.addRect(duwagrect);
m_pen.setStyle(Qt::SolidLine);
m_pen.setColor(Qt::black);
m_pen.setWidth(1);
painter->setPen(m_pen);
painter->drawPath(circle_path);
if(m_thetile->getProperty()->getBlocks())
{
QPainterPath circle_path;
QRect duwagrect(boundingRect().x()+2,boundingRect().y()+2,boundingRect().width()-3,boundingRect().height()-3);
circle_path.addRect(duwagrect);
m_pen.setStyle(Qt::DotLine);
m_pen.setColor(Qt::red);
m_pen.setWidth(2);
painter->setPen(m_pen);
painter->drawPath(circle_path);
}
if(this->isSelected())
{
QPainterPath circle_path;
QRect duwagrect(boundingRect().x()+2,boundingRect().y()+2,boundingRect().width()-3,boundingRect().height()-3);
circle_path.addRect(duwagrect);
m_pen.setStyle(Qt::SolidLine);
m_pen.setColor(Qt::green);
m_pen.setWidth(3);
painter->setPen(m_pen);
painter->drawPath(circle_path);
}
if(option->state & QStyle::State_MouseOver)
{
QPainterPath circle_path;
QRect duwagrect(boundingRect().x()+2,boundingRect().y()+2,boundingRect().width()-3,boundingRect().height()-3);
circle_path.addRect(duwagrect);
m_pen.setStyle(Qt::SolidLine);
m_pen.setColor(Qt::cyan);
m_pen.setWidth(2);
painter->setPen(m_pen);
painter->drawPath(circle_path);
}
}
Problem is that you are showing everything even things not needed.
You should create only visible items (items in some visible region).
Another faster approach is to create custom QGraphicsItem which paints hole map, and paint only visible tiles (no tiles as sub items).

sf::Shader and sf::RectangleShape

I have a question about using the sf::Shape from the SFML graphic library. In my game i use sf::RectangleShapes. For example the UserInterface or the player. Here is a peace of code:
std::unique_ptr<sf::RectangleShape> rect;
sf::RenderTarget &target;
sf::RenderStates &stats;
void SfUIComponents::SfBaseRectangle::draw()
{
target.draw(*rect, stats);
}
When I load the shader with a file like here:
If(shader->loadFromFile(vertex, fragment)){ loaded = true;}
effect->loadFromFile("wave.vert","blur.frag");
Before I draw the rectangle :
void SfShader::activate()
{
stats.shader = shader;
}
The text which I draw too show the effect but the rectangles aren't visible. So ms question without showing more code. Is it possible to use the sfml shader for rectangles which are simple fill with white color?
You can use the shader when drawing into your render.
You can do this by creating a "sf::RenderState", and setting it's shader:
sf::RenderState rs;
sf::Shader effect;
effect.loadFromFile("wave.vert","blur.frag");
rs.shader = &effect;
window.draw(rect , rs);
If you don't need to use more information from the RenderState (like a texture), you can directly pass the shader to the draw function:
sf::Shader effect;
effect.loadFromFile("wave.vert","blur.frag");
window.draw(rect , &effect);

Shape manipulation with openFrameworks

I'm a openFrameworks newbie. I am learning basic 2d drawing which is all great so far. I have drawn a circle using:
ofSetColor(0x333333);
ofFill;
ofCircle(100,650,50);
My question is how do I give the circle a variable name so that I can manipulate in the mousepressed method? I tried adding a name before the ofCircle
theball.ofSetColor(0x333333);
theball.ofFill;
theball.ofCircle(100,650,50);
but get I 'theball' was not declared in this scope error.
As razong pointed out that's not how OF works. OF (to the best of my knowledge) provides a handy wrapper to a lot of OpenGL stuff. So you should use OF calls to effect the current drawing context (as opposed to thinking of a canvas with sprite objects or whatever). I usually integrate that kind of thing into my objects. So lets say you have a class like this...
class TheBall {
protected:
ofColor col;
ofPoint pos;
public:
// Pass a color and position when we create ball
TheBall(ofColor ballColor, ofPoint ballPosition) {
col = ballColor;
pos = ballPosition;
}
// Destructor
~TheBall();
// Make our ball move across the screen a little when we call update
void update() {
pos.x++;
pos.y++;
}
// Draw stuff
void draw(float alpha) {
ofEnableAlphaBlending(); // We activate the OpenGL blending with the OF call
ofFill(); //
ofSetColor(col, alpha); // Set color to the balls color field
ofCircle(pos.x, pos.y, 5); // Draw command
ofDisableAlphaBlending(); // Disable the blending again
}
};
Ok cool, I hope that makes sense. Now with this structure you can do something like the following
testApp::setup() {
ofColor color;
ofPoint pos;
color.set(255, 0, 255); // A bright gross purple
pos.x, pos.y = 50;
aBall = new TheBall(color, pos);
}
testApp::update() {
aBall->update()
}
testApp::draw() {
float alpha = sin(ofGetElapsedTime())*255; // This will be a fun flashing effect
aBall->draw(alpha)
}
Happy programming.
Happy designing.
You can't do it that way. ofCircle is a global drawing method and draws just a circle.
You can declare a variable (or better three int for rgb - since you can't use ofColor as an argument for ofSetColor) that store the color for the circle and modify it in the mousepressed method.
Inside the draw method use your variables for ofSetColor before rendering the circle.