I've recently started learning Qt/QML/C++ and trying to build a very basic 3D scene to rotate the camera around a mesh object.
I'm finding it very difficult to follow the examples and I'm finding the documentation doesn't provide any useful instructions. There doesn't seem to be many tutorials out there either, perhaps I'm looking in the wrong places.
main.cpp
#include <Qt3DQuickExtras/qt3dquickwindow.h>
#include <Qt3DQuick/QQmlAspectEngine>
#include <QGuiApplication>
#include <QtQml>
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
Qt3DExtras::Quick::Qt3DQuickWindow view;
// Expose the window as a context property so we can set the aspect ratio
view.engine()->qmlEngine()->rootContext()->setContextProperty("_window", &view);
view.setSource(QUrl("qrc:/main.qml"));
view.setWidth(800);
view.setHeight(600);
view.show();
return app.exec();
}
main.qml
import Qt3D.Core 2.0
import Qt3D.Render 2.0
import Qt3D.Input 2.0
import Qt3D.Extras 2.0
Entity {
id: sceneRoot
Camera {
id: camera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 25
aspectRatio: _window.width / _window.height
nearPlane : 0.1
farPlane : 1000.0
position: Qt.vector3d( 0, 0.0, 20.0 )
upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
}
OrbitCameraController {
camera: camera
}
components: [
RenderSettings {
activeFrameGraph: ForwardRenderer {
clearColor: Qt.rgba(0, 0.5, 1, 1)
camera: camera
}
},
InputSettings { }
]
PhongMaterial {
id: carMaterial
}
Mesh {
id: carMesh
source: "resources/aventador.obj"
}
Entity {
id: carEntity
components: [ carMesh, carMaterial ]
}
}
How do I get the camera to rotate around the mesh object?
The OrbitCameraController allows to move the camera along an orbital path. To make it rotate around the mesh, you could set the viewCenter of the camera to the position of the mesh (translation of the transform of the entity containing the mesh) and use your keyboard/mouse to rotate it around.
So add:
Transform{
id: carTransform
translation: Qt.vector3d(5.0, 5.0, 5.0) //random values, choose your own
}
and add the transform to the components of the entity.
Change the viewCenter of the camera to
viewCenter: carTransform.translation
you should use a mouse or keyboard to do this .
when you use OrbitCameraController or FirstPersonCameraController
you cant have our control . I use this code instead of OrbitCameraController.
Entity{
id: root
property Camera camera;
property real dt: 0.001
property real linearSpeed: 1
property real lookSpeed: 500
property real zoomLimit: 0.16
MouseDevice {
id: mouseDevice
sensitivity: 0.001 // Make it more smooth
}
MouseHandler {
id: mh
readonly property vector3d upVect: Qt.vector3d(0, 1, 0)
property point lastPos;
property real pan;
property real tilt;
sourceDevice: mouseDevice
onPanChanged: root.camera.panAboutViewCenter(pan, upVect);
onTiltChanged: root.camera.tiltAboutViewCenter(tilt);
onPressed: {
lastPos = Qt.point(mouse.x, mouse.y);
}
onPositionChanged: {
// You can change the button as you like for rotation or translation
if (mouse.buttons === 1){ // Left button for rotation
pan = -(mouse.x - lastPos.x) * dt * lookSpeed;
tilt = (mouse.y - lastPos.y) * dt * lookSpeed;
} else if (mouse.buttons === 2) { // Right button for translate
var rx = -(mouse.x - lastPos.x) * dt * linearSpeed;
var ry = (mouse.y - lastPos.y) * dt * linearSpeed;
camera.translate(Qt.vector3d(rx, ry, 0))
} else if (mouse.buttons === 3) { // Left & Right button for zoom
ry = (mouse.y - lastPos.y) * dt * linearSpeed
zoom(ry)
}
lastPos = Qt.point(mouse.x, mouse.y)
}
onWheel: {
zoom(wheel.angleDelta.y * dt * linearSpeed)
}
function zoom(ry) {
if (ry > 0 && zoomDistance(camera.position, camera.viewCenter) < zoomLimit) {
return
}
camera.translate(Qt.vector3d(0, 0, ry), Camera.DontTranslateViewCenter)
}
function zoomDistance(posFirst, posSecond) {
return posSecond.minus(posFirst).length()
}
}}
create a new qml class and call it for example SOrbitCameraController or anything you want then use it instead of OrbitCameraController and take camera to this class.
I know this is an old post but since I found my answer and this stumped me too, here is what I adjusted:
I found that all I needed to do was to set the up Vector to zero, I am writing with pyqt so mine looked like this:
camera.setUpVector(QVector3D(0.0, 0.0, 0.0))
The reason being that after this I was able to lock the right-mouse button controls and rotate around the mesh with the left-mouse button controls.
Related
I am currently working on a project that has a spinning object. The object's rotation speed is changed and its direction of rotation is changed (forwards/backward). After doing some research I came across RotationAnimator on the QT documentation, it seems to have everything I need and I can easily reverse direction. The only problem is that is in QML. My program is entirely in Qt's C++.
I tried an attempt at using QVariantAnimation (documentation here)but every way I have tried there is always some problem. Changing speed is relatively easy, just fooAnimation->setLoop(-1) (loop indefinitely) and change the duration of the animation. The problem I run into is looping forwards AND backward. Or in other terms, changing direction. I have attempted to change the direction of the animation using fooAnimation->setDirection(<Direction goes here>), but if I run the animation in reverse long enough it will stop the animation entirely. It will only loop forwards. That's the key part of the problem.
So I tried alternative ways to change the direction. One idea was changing the endValue from 360 degrees to -360 using fooAnimation->setEndValue(<value goes here>) when I want to reverse, but this has an adverse effect. If I were to swap the direction, the current rotation: let's say it's 110, will be inverted. So now it suddenly jumps to -110 and starts from there. This leads to very jittery rotation as every time a direction swap happens it teleports the rotation of the object.
This seems like a pretty simple thing to implement, rotating an object that can have its rotation speed and direction changed, but I just can't wrap my head around how I could implement it with animations.
A side note here is that all this is being done in Qt 3D so I can grab rotation and other properties from the rotating object (or in this case its QTransform)
There are multiple ways to achieve this effect. On this Qt/QML demo, I used an approach based on NumberAnimation to rotate the cube.
These are the key ideas behind it:
The cube has a Transform component that defines the 4x4 matrix that Qt3D uses to place the cube in the world using predefined rotation and scale values. The NumberAnimation simply changes the rotation angle used on this transform to make the cube rotate on the screen;
The rotation speed is defined by the duration property of that NumberAnimation: a smaller value makes the cube rotate faster. The cube takes 5 seconds to rotate from 0 to 360 degrees;
Changing the rotation speed means recalculating the duration of the animation based on how far away from 0 degrees the current rotation angle is. The animation needs to be restarted whenever we change ANY NumberAnimation properties;
When the direction of the rotation is changed, we store the current rotation angle in cubeTransform.startAngle and update the to and from properties of NumberAnimation along with duration so the cube can be rotated in a new direction starting from the current angle.
I added a few buttons to the UI to change the direction and speed in runtime:
main.qml:
import QtQuick 2.12 as QQ2
import QtQuick.Controls 2.12
import QtQuick.Scene3D 2.12
import Qt3D.Core 2.0
import Qt3D.Render 2.0
import Qt3D.Input 2.0
import Qt3D.Extras 2.0
import Qt3D.Logic 2.0
QQ2.Item {
id: windowId
width: 800
height: 800
property real cubeScale: 100.0
property int rotationSpeed: 5000
property real speedFactor: 1.0
property bool clockwiseRotation: true
Scene3D {
id: scene3D
anchors.fill: parent
aspects: ["input", "logic"]
focus: true
cameraAspectRatioMode: Scene3D.AutomaticAspectRatio
Entity {
id: rootEntity
Camera {
id: camera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 45
nearPlane : 0.1
farPlane : 100000.0
position: Qt.vector3d(0.0, 0.0, 500.0)
viewCenter: Qt.vector3d(0.0, 0.0, 0.0)
upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
}
components: [
RenderSettings {
activeFrameGraph: ForwardRenderer {
camera: camera
clearColor: "silver"
}
},
InputSettings { }
]
Entity {
id: lightEntity
enabled: true
DirectionalLight {
id: infiniteLight
color: "white"
intensity: 1.0
worldDirection: Qt.vector3d(0, 0, 0)
}
Transform {
id: infiniteLightTransform
translation: Qt.vector3d(0.0, 0.0, 500.0)
}
components: [ infiniteLight, infiniteLightTransform ]
}
Entity {
id: cubeEntity
CuboidMesh {
id: cubeMesh
}
PhongMaterial {
id: cubeMaterial
ambient: "red"
diffuse: Qt.rgba(1.0, 1.0, 1.0, 1.0)
}
Transform {
id: cubeTransform
property real angle: 0.0
property real startAngle: 0.0
matrix: {
var m = Qt.matrix4x4();
m.rotate(cubeTransform.angle, Qt.vector3d(0, 1, 0));
m.translate(Qt.vector3d(0, 0, 0));
m.scale(windowId.cubeScale, windowId.cubeScale, windowId.cubeScale);
return m;
}
}
components: [ cubeMesh, cubeMaterial, cubeTransform ]
}
QQ2.NumberAnimation {
id: cubeAnimation
property int startPos: {
if (cubeTransform.startAngle === 0)
{
if (windowId.clockwiseRotation === true)
return 0;
return 360;
}
return cubeTransform.startAngle;
}
property int endPos: (windowId.clockwiseRotation === true) ? 360 : 0
target: cubeTransform
property: "angle"
duration: windowId.rotationSpeed / windowId.speedFactor
from: cubeAnimation.startPos
to: cubeAnimation.endPos
loops: 1
running: true
onStarted: {
console.log("onStarted");
}
onFinished: {
console.log("onFinished");
// reset the starting angle
cubeTransform.startAngle = 0;
// reset the duration of the animation
cubeAnimation.duration = windowId.rotationSpeed / windowId.speedFactor;
// the animation is currently stopped, run it again
cubeAnimation.running = true;
}
}
}
}
QQ2.Row {
anchors.fill: parent
Button {
text: "Change Direction"
onClicked: {
// pause the animation
cubeAnimation.pause();
// invert rotation
windowId.clockwiseRotation = !windowId.clockwiseRotation;
// store current angle as the starting angle for the rotation
cubeTransform.startAngle = cubeTransform.angle;
// update the remaining time to avoid changing the rotation speed
let progressPercentage = cubeTransform.startAngle / 360.0;
if (windowId.clockwiseRotation)
progressPercentage = 1.0 - progressPercentage;
cubeAnimation.duration = (windowId.rotationSpeed / windowId.speedFactor) * progressPercentage;
// restart the animation from this angle using a new direction
cubeAnimation.restart();
}
}
Button {
text: "1x"
highlighted: (windowId.speedFactor === 1.0)
onClicked: changeSpeed(1.0)
}
Button {
text: "2x"
highlighted: (windowId.speedFactor === 2.0)
onClicked: changeSpeed(2.0)
}
}
function changeSpeed(newSpeed) {
windowId.speedFactor = newSpeed;
// pause the animation
cubeAnimation.pause();
// store current angle as the starting angle for the rotation
cubeTransform.startAngle = cubeTransform.angle;
// update the remaining time to avoid changing the rotation speed
let progressPercentage = cubeTransform.startAngle / 360.0;
if (windowId.clockwiseRotation)
progressPercentage = 1.0 - progressPercentage;
cubeAnimation.duration = (windowId.rotationSpeed / windowId.speedFactor) * progressPercentage;
// restart the animation from this angle using a new direction
cubeAnimation.restart();
}
}
What I actually try to achieve:
I'd like to draw text with a gradient vertical color. I found this solution, but it doesn't quite fit for me, as it has black square around the gradient font in my case - don't know how to get rid of it, so I started simple (the irrelevant part) question to understand better the physics of blending and frame buffer in opengl and libgdx
What I was trying to understand, irrelevant to my goal:
I have a texture with a white square on it, I draw it on top of red background. I am trying to draw a green square on top of the white one, the green square partially covers the white one, and partially on top of the red background (see picture below).
My intention is: the white area, that is behind of the green square should be painted in green color, but all red background should not be affected and stayed unchanged (red as it is).
How can I do this?
package com.mygdx.game;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
public class Game extends ApplicationAdapter {
SpriteBatch batch;
Texture img;
private int height;
private int width;
private ShapeRenderer shapeRenderer;
#Override
public void create() {
batch = new SpriteBatch();
img = new Texture("white.png");
width = Gdx.graphics.getWidth();
height = Gdx.graphics.getHeight();
shapeRenderer = new ShapeRenderer();
shapeRenderer.setAutoShapeType(true);
}
#Override
public void render() {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(img, width / 7, height / 4);
batch.end();
Gdx.gl.glEnable(GL20.GL_BLEND);
Gdx.gl.glBlendFunc(GL20.GL_ONE, GL20.GL_SRC_COLOR);
shapeRenderer.begin();
shapeRenderer.set(ShapeRenderer.ShapeType.Filled);
shapeRenderer.setColor(Color.GREEN);
shapeRenderer.rect(width / 2 - 100, height / 4 - 50, 200, 200);
shapeRenderer.end();
Gdx.gl.glDisable(GL20.GL_BLEND);
}
#Override
public void dispose() {
batch.dispose();
img.dispose();
}
}
Ideally, the green square should not be transparent anyhow, just should block white where it hides the white area.
The output I'm getting:
Update:
I mark #Xoppa 's answer as correct, as it solves my original question with the following result:
You could indeed use some kind of mask to blend it using a square. For that you can first render the text to the stencil buffer using a custom shader that discards fragments with an alpha value below a certain threshold. After that you can render the square using the stencil function to only affect the fragments "touched" by the text. Note that this does involve multiple render calls though and therefore adds complexity to your calling code as well.
However, you say that you actually just want to render text using gradient. For that you don't need such complex approach and can simply apply the gradient within the same render call.
When you draw text, you actually render many little squares, for each character in the text one square. Each of this square has a textureregion applied that contains the character on a transparent background. If you open the font image (e.g. this is the default), then you'll see this source image.
Just like you can apply a gradient to a normal square, you can also apply a gradient to each of those individual squares that make up the text. There are multiple ways to do that. Which best suits depends on the use-case. For example if you need a horizontal gradient or have multiline text, then you need some additional steps. Since you didn't specify this, I'm going to assume that you want to apply a vertical gradient on a single line of text:
public class MyGdxGame extends ApplicationAdapter {
public static class GradientFont extends BitmapFont {
public static void applyGradient(float[] vertices, int vertexCount, float color1, float color2, float color3, float color4) {
for (int index = 0; index < vertexCount; index += 20) {
vertices[index + SpriteBatch.C1] = color1;
vertices[index + SpriteBatch.C2] = color2;
vertices[index + SpriteBatch.C3] = color3;
vertices[index + SpriteBatch.C4] = color4;
}
}
public GlyphLayout drawGradient(Batch batch, CharSequence str, float x, float y, Color topColor, Color bottomColor) {
BitmapFontCache cache = getCache();
float tc = topColor.toFloatBits();
float bc = bottomColor.toFloatBits();
cache.clear();
GlyphLayout layout = cache.addText(str, x, y);
for (int page = 0; page < cache.getFont().getRegions().size; page++) {
applyGradient(cache.getVertices(page), cache.getVertexCount(page), bc, tc, tc, bc);
}
cache.draw(batch);
return layout;
}
}
SpriteBatch batch;
GradientFont font;
float topColor;
float bottomColor;
#Override
public void create () {
batch = new SpriteBatch();
font = new GradientFont();
}
#Override
public void render () {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
font.drawGradient(batch, "Hello world", 0, 100, Color.GREEN, Color.BLUE);
batch.end();
}
#Override
public void dispose () {
batch.dispose();
font.dispose();
}
}
Btw, to get better answers you should include the actual problem you are trying to solve, instead of focusing on what you think is the solution. See also: https://stackoverflow.com/help/asking.
You can fake blending by doing some math here's what I came up with:
import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Rectangle;
public class CalculatedMask extends Game {
private SpriteBatch batch; // The SpriteBatch to draw the white image
private ShapeRenderer renderer; // The ShapeRenderer to draw the green rectangle
private Texture img; // The texture of the image
private Rectangle imgBounds; // The bounds of the image
private Rectangle squareBounds; // The bounds of the square
private float width; // The width of the screen
private float height; // The height of the screen
private float squareX; // The x position of the green square
private float squareY; // The y position of the green square
private float squareWidth; // The width of the green square
private float squareHeight; // The height of the green square
#Override
public void create() {
width = Gdx.graphics.getWidth();
height = Gdx.graphics.getHeight();
batch = new SpriteBatch();
renderer = new ShapeRenderer();
renderer.setAutoShapeType(true);
img = new Texture("pixel.png"); // A 1x1 white pixel png
imgBounds = new Rectangle(); // The white image bounds
imgBounds.setPosition(width / 7f, height / 4f); // Position the white image bounds
imgBounds.setSize(400f, 300f); // Scale the white image bounds
calculateRectangle();
}
private void calculateRectangle() {
// Here we define the green rectangle's original position and size
squareBounds = new Rectangle();
squareX = width / 2f - 300f;
squareY = height / 4f - 50f;
squareWidth = 200f;
squareHeight = 200f;
// Adjust green square x position
squareBounds.x = MathUtils.clamp(squareX, imgBounds.x, imgBounds.x + imgBounds.width);
// Adjust green square y position
squareBounds.y = MathUtils.clamp(squareY, imgBounds.y, imgBounds.y + imgBounds.height);
// Adjust green square width
if (squareX < imgBounds.x) {
squareBounds.width = Math.max(squareWidth + squareX - imgBounds.x, 0f);
} else if (squareX + squareWidth > imgBounds.x + imgBounds.width) {
squareBounds.width = Math.max(imgBounds.width - squareX + imgBounds.x, 0f);
} else {
squareBounds.width = squareWidth;
}
// Adjust green square height
if (squareY < imgBounds.y) {
squareBounds.height = Math.max(squareHeight + squareY - imgBounds.y, 0f);
} else if (squareY + squareHeight > imgBounds.y + imgBounds.height) {
squareBounds.height = Math.max(imgBounds.height - squareY + imgBounds.y, 0f);
} else {
squareBounds.height = squareHeight;
}
}
#Override
public void render() {
// Clear previous frame
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// Draw the white image
batch.begin();
batch.draw(img, imgBounds.x, imgBounds.y, imgBounds.width, imgBounds.height);
batch.end();
// Draw the green rectangle without affecting background
renderer.begin();
renderer.setColor(Color.GREEN);
// Debug so we can see the real green rectangle
renderer.set(ShapeRenderer.ShapeType.Line);
renderer.rect(squareX, squareY, squareWidth, squareHeight);
// Draw the modified green rectangle
renderer.set(ShapeRenderer.ShapeType.Filled);
renderer.rect(squareBounds.x, squareBounds.y, squareBounds.width, squareBounds.height);
renderer.end();
}
}
And the results are:
And with:
squareX = width / 2f + 100f;
squareY = height / 4f + 150f;
I'm sure there is an answer to this on the web but I can't find it.
I'm importing a scene from Blender that has multiple meshes, into OpenTK.
The library I'm using to import is Assimp-net, and the file format is Collada (.dae).
I have created a spaceship with multiple parts, each part being a mesh.
Now when I import and draw, the geometry of the objects looks fine and materials work as expected. However, the different parts are not rotated, scaled, or translated as they appear in Blender. What happens is the different parts are not connected, and some appear larger/smaller than they should, in the wrong place etc.
Is there a setting I'm missing when I export from Blender, or is there some Assimp member/function I can use to transform the meshes before I render them?
Importing the file:
string filename = #"C:\Path\ship.dae";
Scene ship;
//Create a new importer
AssimpImporter importer = new AssimpImporter();
//This is how we add a configuration (each config is its own class)
NormalSmoothingAngleConfig config = new NormalSmoothingAngleConfig(66.0f);
importer.SetConfig(config);
//Import the model
ship = importer.ImportFile(filename, PostProcessPreset.TargetRealTimeMaximumQuality);
//End of example
importer.Dispose();
Drawing the meshes(entire "RenderFrame" event handler in OpenTK):
// Clear color/depth buffers
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
// Define world space
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.Ortho(-15.0, 15.0, -15.0, 15.0, 15.0, -15.0);
// Rotate around X and Y axes for better viewing
rotateX(xrot);
rotateY(yrot);
GL.Enable(EnableCap.ColorMaterial);
var rootnode = wes10.RootNode;
foreach (Node node in rootnode.Children)
{
//for each node, do
GL.MatrixMode(MatrixMode.Modelview); //ensure your current matrix is the model matrix.
GL.PushMatrix(); //save current model matrix so you can undo next transformations;
var meshIndices = node.MeshIndices;
if (meshIndices == null)
continue;
else
{
Matrix4d convertedTransform = new Matrix4d();
getConvertedMatrix(node.Transform, ref convertedTransform);
GL.MultMatrix(ref convertedTransform);
GL.Begin(BeginMode.Triangles);
foreach (uint i in meshIndices)
{
Mesh mesh = wes10.Meshes[i];
Material mat = wes10.Materials[mesh.MaterialIndex];
// Material setup
var spec_color = mat.ColorSpecular;
var amb_color = mat.ColorAmbient;
var diff_color = mat.ColorDiffuse;
float[] mat_specular = { spec_color.R, spec_color.G, spec_color.B, spec_color.A };
float[] mat_ambient = { amb_color.R, amb_color.G, amb_color.B, amb_color.A };
float[] mat_diffuse = { diff_color.R, diff_color.G, diff_color.B, diff_color.A };
float[] mat_shininess = { 0.0f };
GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Specular, mat_specular);
GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Ambient, mat_ambient);
GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Diffuse, mat_diffuse);
GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Shininess, mat_shininess);
foreach (Face face in mesh.Faces)
{
foreach (uint indice in face.Indices)
{
var normal = mesh.Normals[indice];
var pos = mesh.Vertices[indice];
//var tex = mesh.GetTextureCoords(0)[v];
//GL.TexCoord2(tex.X, tex.Y);
GL.Normal3(normal.X, normal.Y, normal.Z);
GL.Vertex3(pos.X, pos.Y, pos.Z);
}
}
}
}
GL.PopMatrix();
}
GL.End();
game.SwapBuffers();
Updated to use suggestions.
In the c example, there is a transformation matrix per node...
aiMultiplyMatrix4(trafo,&nd->mTransformation);
Check this:
Data structure
scene graph.
If you don't know what to do with that matrix, check this to learn about matrix stack. (Be aware that modern OpenGL recommand to implement your own transformation matrix)
Golobaly, you need the folowing steps for rendering (read the c example for details):
//for each node, do
glMatrixMode (GL_MODELVIEW); //ensure your current matrix is the model matrix.
glPushMatrix (); //save current model matrix so you can undo next transformations;
glMultMatrixf(Transformation);//apply your node matrix
//render your node, in your example it's surely a mesh
glPopMatrix (); //restore model matrix
I use Box2D as phisics engine and QtQuick as visualizer.
I setted up simple scene with falling small rectangles and platform to test collisions
// the platform block
Body {
id: block
bodyType: Body.Kinematic
width:200
height:20
transformOrigin: Body.Center
fixtures: Box {
anchors.fill: parent
friction: 1
density: 1
}
Rectangle {
anchors.fill: parent
color: "yellow"
border.color: "blue"
}
MouseArea {
anchors.fill: parent
onClicked: {
block.rotation += 20
}
}
}
In QML I can set center of rotation:
transformOrigin: Body.Center
By default transformOrigin is top-left corner of object and in this case everything paintet fine. But when i move origin to center in QML it is going wrong as decribed at attached image
This is part of code when it get coords from Box2D and paint QML object
//getting coordination and angle of Box2D object
const b2Vec2 position = mBody->GetPosition();
const float32 angle = mBody->GetAngle();
const qreal newX = position.x * scaleRatio;
const qreal newY = -position.y * scaleRatio;
const qreal newRotation = -(angle * 360.0) / (2 * b2_pi);
// paint QML object at received coords
setX(newX);
setY(newY);
setRotation(newRotation);
The problem is than Box2D rotates object with origin in top-left corner but QML object painted with origin in his center. So QML object is not syncronized with Box2D and falling rectangle painter in wrong place. I maked printscreen but actually box of Box2D is not visible, I added it to understand the problem.
And my question - how can I set origin point in Box2D?
To keep the code simple, and because qml-box2d is an unfinished library, transformOrigin set to TopLeft (the default for a Body) is currently the only supported value.
However, a Box2D body doesn't need to have a size itself. Only the size of its fixtures is relevant. If you set a 0-size on the body its transformOrigin no longer matters. If you then center the fixture on the body, the rotation of the body effectively applies to the center of the fixture.
I've adapted your simple scene using this approach as follows:
// the platform block
Body {
id: block
bodyType: Body.Kinematic
fixtures: Box {
id: boxFixture
anchors.centerIn: parent
width:200
height:20
friction: 1
density: 1
}
Rectangle {
anchors.fill: boxFixture
color: "yellow"
border.color: "blue"
}
MouseArea {
anchors.fill: boxFixture
onClicked: {
block.rotation += 20
}
}
}
Of course, ideally qml-box2d would eventually handle all possible values of transformOrigin regardless of the size of the body.
I am trying to drag around a circle with raphael.js, but it seems that cx and cy does not get updated for the circle in order to set the correct new positions of the circles.
The code can be seen and tested here: http://jsfiddle.net/MXFWW/
As you've discovered, applying a transformation to a Raphael object does not alter its positional attributes.
Check out the ellipsis syntax in the transform method. Because transformations are such a headache, I prefer in simple cases to directly alter the attributes. You just have to remember where you started in the dragStart function using the .data() method to store arbitrary data.
var paper = Raphael(0, 0, 320, 320);
var innerC = paper.circle(320 / 2, 320 / 2, 20);
innerC.attr("stroke", "#000");
innerC.attr("fill", "#000");
var dragMove = function (dx, dy, x, y, e) {
console.log(innerC.attr('cx'));
this.attr("cx", this.data("ox") + dx);
this.attr("cy", this.data("oy") + dy);
this.animate({
"fill-opacity": 1
}, 500);
},
dragStart = function (x, y) {
this.data("ox", this.attr("cx"));
this.data("oy", this.attr("cy"));
},
dragEnd = function () {
this.animate({
"fill-opacity": 1
}, 500);
};
innerC.drag(dragMove, dragStart, dragEnd);
jsFiddle