File indexing completed on 2024-04-21 14:43:24

0001 /*
0002  * box2ddebugdraw.cpp
0003  * Copyright (c) 2010 Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
0004  * Copyright (c) 2014 Moukhlynin Ruslan <ruslan@khvmntk.ru>
0005  *
0006  * This file is part of the Box2D QML plugin.
0007  *
0008  * This software is provided 'as-is', without any express or implied warranty.
0009  * In no event will the authors be held liable for any damages arising from
0010  * the use of this software.
0011  *
0012  * Permission is granted to anyone to use this software for any purpose,
0013  * including commercial applications, and to alter it and redistribute it
0014  * freely, subject to the following restrictions:
0015  *
0016  * 1. The origin of this software must not be misrepresented; you must not
0017  *    claim that you wrote the original software. If you use this software in
0018  *    a product, an acknowledgment in the product documentation would be
0019  *    appreciated but is not required.
0020  *
0021  * 2. Altered source versions must be plainly marked as such, and must not be
0022  *    misrepresented as being the original software.
0023  *
0024  * 3. This notice may not be removed or altered from any source distribution.
0025  */
0026 
0027 #define CIRCLE_SEGMENTS_COUNT 32
0028 #define LINE_WIDTH 1
0029 
0030 #include "box2ddebugdraw.h"
0031 
0032 #include "box2dworld.h"
0033 
0034 #include <Box2D.h>
0035 
0036 #include <QPainter>
0037 #include <QSGNode>
0038 #include <QSGFlatColorMaterial>
0039 #include <QtCore/qmath.h>
0040 
0041 class DebugDraw : public b2Draw
0042 {
0043 public:
0044     DebugDraw(QSGNode *root, Box2DWorld &world);
0045 
0046     void draw();
0047 
0048     void DrawPolygon(const b2Vec2 *vertices, int32 vertexCount,
0049                      const b2Color &color);
0050     void DrawSolidPolygon(const b2Vec2 *vertices, int32 vertexCount,
0051                           const b2Color &color);
0052     void DrawCircle(const b2Vec2 &center, float32 radius,
0053                     const b2Color &color);
0054     void DrawSolidCircle(const b2Vec2 &center, float32 radius,
0055                          const b2Vec2 &axis, const b2Color &color);
0056     void DrawSegment(const b2Vec2 &p1, const b2Vec2 &p2,
0057                      const b2Color &color);
0058     void DrawTransform(const b2Transform &xf);
0059 
0060     void setAxisScale(qreal axisScale);
0061 
0062 private:
0063     QSGNode *mRoot;
0064     Box2DWorld &mWorld;
0065     qreal mAxisScale;
0066 
0067     QSGNode *createNode(QSGGeometry *geometry, const QColor &color, QSGNode *parent = 0);
0068 };
0069 
0070 DebugDraw::DebugDraw(QSGNode *root, Box2DWorld &world) :
0071     mRoot(root),
0072     mWorld(world)
0073 {
0074 }
0075 
0076 void DebugDraw::draw()
0077 {
0078     mWorld.world().SetDebugDraw(this);
0079     mWorld.world().DrawDebugData();
0080     mWorld.world().SetDebugDraw(0);
0081 }
0082 
0083 static QColor toQColor(const b2Color &color)
0084 {
0085     return QColor(color.r * 255,
0086                   color.g * 255,
0087                   color.b * 255,
0088                   color.a * 255);
0089 }
0090 
0091 QSGNode *DebugDraw::createNode(QSGGeometry *geometry, const QColor &color, QSGNode *parent)
0092 {
0093     QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
0094     material->setColor(color);
0095 
0096     QSGGeometryNode *node = new QSGGeometryNode;
0097     node->setGeometry(geometry);
0098     node->setFlag(QSGNode::OwnsGeometry);
0099     node->setMaterial(material);
0100     node->setFlag(QSGNode::OwnsMaterial);
0101 
0102     if (parent)
0103         parent->appendChildNode(node);
0104     else
0105         mRoot->appendChildNode(node);
0106     return node;
0107 }
0108 
0109 void DebugDraw::DrawPolygon(const b2Vec2 *vertices,
0110                             int32 vertexCount,
0111                             const b2Color &color)
0112 {
0113     QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(),
0114                                             vertexCount);
0115     geometry->setDrawingMode(GL_LINE_LOOP);
0116     geometry->setLineWidth(LINE_WIDTH);
0117 
0118     QSGGeometry::Point2D *points = geometry->vertexDataAsPoint2D();
0119     for (int i = 0; i < vertexCount; ++i) {
0120         QPointF point = mWorld.toPixels(vertices[i]);
0121         points[i].set(point.x(), point.y());
0122     }
0123 
0124     createNode(geometry, toQColor(color));
0125 }
0126 
0127 void DebugDraw::DrawSolidPolygon(const b2Vec2 *vertices,
0128                                  int32 vertexCount,
0129                                  const b2Color &color)
0130 {
0131     QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(),
0132                                             vertexCount);
0133     geometry->setDrawingMode(GL_TRIANGLE_FAN);
0134     geometry->setLineWidth(LINE_WIDTH);
0135 
0136     QSGGeometry::Point2D *points = geometry->vertexDataAsPoint2D();
0137     for (int i = 0; i < vertexCount; ++i) {
0138         QPointF point = mWorld.toPixels(vertices[i]);
0139         points[i].set(point.x(), point.y());
0140     }
0141 
0142     createNode(geometry, toQColor(color));
0143 }
0144 
0145 void DebugDraw::DrawCircle(const b2Vec2 &center,
0146                            float32 radius,
0147                            const b2Color &color)
0148 {
0149     QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(),
0150                                             CIRCLE_SEGMENTS_COUNT);
0151     geometry->setDrawingMode(GL_LINE_LOOP);
0152     geometry->setLineWidth(LINE_WIDTH);
0153 
0154     QPointF centerInPixels = mWorld.toPixels(center);
0155     qreal radiusInPixels = mWorld.toPixels(radius);
0156 
0157     QSGGeometry::Point2D *points = geometry->vertexDataAsPoint2D();
0158     for (int i = 0; i < CIRCLE_SEGMENTS_COUNT; ++i) {
0159         float theta = i * 2 * M_PI / (CIRCLE_SEGMENTS_COUNT - 2);
0160         points[i].set(centerInPixels.x() + radiusInPixels * qCos(theta),
0161                       centerInPixels.y() + radiusInPixels * qSin(theta));
0162     }
0163 
0164     createNode(geometry, toQColor(color));
0165 }
0166 
0167 void DebugDraw::DrawSolidCircle(const b2Vec2 &center, float32 radius,
0168                                 const b2Vec2 &axis, const b2Color &color)
0169 {
0170 
0171     QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(),
0172                                             CIRCLE_SEGMENTS_COUNT);
0173     geometry->setDrawingMode(GL_TRIANGLE_FAN);
0174     geometry->setLineWidth(LINE_WIDTH);
0175 
0176     QPointF centerInPixels = mWorld.toPixels(center);
0177     QPointF axisInPixels = mWorld.toPixels(axis);
0178     qreal radiusInPixels = mWorld.toPixels(radius);
0179     axisInPixels.setX(centerInPixels.x() + radius * axisInPixels.x());
0180     axisInPixels.setY(centerInPixels.y() + radius * axisInPixels.y());
0181 
0182     QSGGeometry::Point2D *points = geometry->vertexDataAsPoint2D();
0183     points[0].set(centerInPixels.x(), centerInPixels.y());
0184     for (int i = 1; i < CIRCLE_SEGMENTS_COUNT; ++i) {
0185         float theta = i * 2 * M_PI / (CIRCLE_SEGMENTS_COUNT - 2);
0186         points[i].set(centerInPixels.x() + radiusInPixels * qCos(theta),
0187                       centerInPixels.y() + radiusInPixels * qSin(theta));
0188     }
0189     QSGNode * node = createNode(geometry,toQColor(color));
0190 
0191     QSGGeometry *axisGeometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 2);
0192     axisGeometry->setDrawingMode(GL_LINES);
0193     axisGeometry->setLineWidth(LINE_WIDTH);
0194 
0195     axisGeometry->vertexDataAsPoint2D()[0].set(centerInPixels.x(), centerInPixels.y());
0196     axisGeometry->vertexDataAsPoint2D()[1].set(axisInPixels.x(), axisInPixels.y());
0197     createNode(axisGeometry, qRgb(200, 64, 0), node);
0198 }
0199 
0200 void DebugDraw::DrawSegment(const b2Vec2 &p1,
0201                             const b2Vec2 &p2,
0202                             const b2Color &color)
0203 {
0204     QPointF p1InPixels = mWorld.toPixels(p1);
0205     QPointF p2InPixels = mWorld.toPixels(p2);
0206 
0207     QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 2);
0208     geometry->setDrawingMode(GL_LINES);
0209     geometry->setLineWidth(LINE_WIDTH);
0210 
0211     geometry->vertexDataAsPoint2D()[0].set(p1InPixels.x(), p1InPixels.y());
0212     geometry->vertexDataAsPoint2D()[1].set(p2InPixels.x(), p2InPixels.y());
0213 
0214     createNode(geometry, toQColor(color));
0215 }
0216 
0217 void DebugDraw::DrawTransform(const b2Transform &xf)
0218 {
0219 
0220     QPointF p1 = mWorld.toPixels(xf.p);
0221     QPointF p2 = mWorld.toPixels(xf.q.GetXAxis());
0222     p2 = QPointF(p1.x() + mAxisScale * p2.x(),
0223                  p1.y() + mAxisScale * p2.y());
0224 
0225     QSGGeometry *geometryX = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 2);
0226     geometryX->setDrawingMode(GL_LINES);
0227     geometryX->setLineWidth(LINE_WIDTH);
0228     geometryX->vertexDataAsPoint2D()[0].set(p1.x(), p1.y());
0229     geometryX->vertexDataAsPoint2D()[1].set(p2.x(), p2.y());
0230     createNode(geometryX,Qt::blue);
0231 
0232     p2 = mWorld.toPixels(xf.q.GetYAxis());
0233     p2 = QPointF(p1.x() + mAxisScale * p2.x(),
0234                  p1.y() + mAxisScale * p2.y());
0235 
0236     QSGGeometry *geometryY = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 2);
0237     geometryY->setDrawingMode(GL_LINES);
0238     geometryY->setLineWidth(LINE_WIDTH);
0239     geometryY->vertexDataAsPoint2D()[0].set(p1.x(), p1.y());
0240     geometryY->vertexDataAsPoint2D()[1].set(p2.x(), p2.y());
0241 
0242     createNode(geometryY, Qt::yellow);
0243 }
0244 
0245 void DebugDraw::setAxisScale(qreal axisScale)
0246 {
0247     mAxisScale = axisScale;
0248 }
0249 
0250 Box2DDebugDraw::Box2DDebugDraw(QQuickItem *parent) :
0251     QQuickItem (parent),
0252     mWorld(0),
0253     mAxisScale(0.5),
0254     mFlags(Everything)
0255 {
0256     setFlag(QQuickItem::ItemHasContents, true);
0257     setWorld(Box2DWorld::defaultWorld());
0258 }
0259 
0260 void Box2DDebugDraw::setAxisScale(qreal axisScale)
0261 {
0262     if (mAxisScale != axisScale) {
0263         mAxisScale = axisScale;
0264         emit axisScaleChanged();
0265     }
0266 }
0267 
0268 void Box2DDebugDraw::setFlags(DebugFlag flags)
0269 {
0270     if (mFlags != flags) {
0271         mFlags = flags;
0272         emit flagsChanged();
0273     }
0274 }
0275 
0276 void Box2DDebugDraw::setWorld(Box2DWorld *world)
0277 {
0278     if (mWorld == world)
0279         return;
0280 
0281     if (mWorld)
0282         mWorld->disconnect(this);
0283 
0284     mWorld = world;
0285 
0286     if (mWorld)
0287         connect(mWorld, SIGNAL(stepped()), SLOT(onWorldStepped()));
0288 
0289     emit worldChanged();
0290 }
0291 
0292 QSGNode *Box2DDebugDraw::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
0293 {
0294     if (oldNode)
0295         delete oldNode;
0296 
0297     if (!mWorld)
0298         return 0;
0299 
0300     QSGTransformNode *root = new QSGTransformNode;
0301     DebugDraw debugDraw(root, *mWorld);
0302     debugDraw.SetFlags(mFlags);
0303     debugDraw.setAxisScale(mAxisScale);
0304     debugDraw.draw();
0305     return root;
0306 }
0307 
0308 void Box2DDebugDraw::onWorldStepped()
0309 {
0310     if (isVisible() && opacity() > 0)
0311         update();
0312 }