Error draw Sprite in cocos2dx v3.6 - c++

I'm new in cocos2dx. Followed this tutorial.
I create simple test with cocos2dx v3.6.
Bullet.h:
using namespace cocos2d;
class Bullet : public Sprite{
public:
Bullet();
~Bullet();
static Bullet* createBullet();
void setBullet( Vec2 pos, Vec2 vel , int Lev , float rotate);
private:
void moveFinish();
Vec2 velocity;
// static SpriteFrameCache * fr = SpriteFrameCache::getInstance();
};
Bullet.ccp
#include "Bullet.h"
Bullet::Bullet(){}
Bullet::~Bullet(){}
void Bullet::setBullet( Vec2 pos, Vec2 vel , int Lev , float rotate){
SpriteFrameCache * frc = SpriteFrameCache::getInstance();
frc->addSpriteFramesWithFile("bullet.plist","bullet.png");
SpriteFrame * sf = frc->getSpriteFrameByName("bullet8.png");
setSpriteFrame(sf);
setPosition(pos);
setRotation(rotate);
velocity = vel;
// schedule(update, 0.2);
//
// FiniteTimeAction * actionMove = MoveTo::create(10.0f, Vec2(1000, 1000));
// FiniteTimeAction * actionDone = CallFuncN::create(callfunc_selector(Bullet::moveFinish));
//
// runAction(Sequence::create(actionMove, actionDone, NULL));
}
void Bullet::moveFinish(){
}
Bullet* Bullet::createBullet(){
Bullet* sp = new Bullet();
if (sp->create()) {
sp->autorelease();
return sp;
}
CC_SAFE_DELETE(sp);
return NULL;
}
Main scene :
....
Bullet * bl = Bullet::createBullet();
bl->setBullet(gun->getPosition(), Vec2(5, 5), 2, gun->getRotation());
addChild(bl);
....
When run it show error:
Assert failed: Invalid GLProgramState Assertion failed: (shader),
function init, file
/Users/tuan/zzijkline/cocos2d/cocos/renderer/CCQuadCommand.cpp, line
50.
How can I fix it?

Your createBullet() seems wrong. You should be instantiating the Sprite from it and you should be loading the SpriteFrameCache from outside your Bullet class.
setBullet() should only be handling position, rotation and velocity.
Bullet* Bullet::create() {
Bullet* sp = new Bullet();
if (sp->initWithSpriteFrameName("bullet8.png")) {
sp->autorelease();
return sp;
}
CC_SAFE_DELETE(sp);
return NULL;
}
void Bullet::setBullet( Vec2 pos, Vec2 vel , int Lev , float rotate) {
setSpriteFrame(sf);
setPosition(pos);
setRotation(rotate);
velocity = vel;
}

Related

Dynamic Light effect with Cocos2d-x 4.0 and C++

I've been trying to create a dynamic light effect with cocos2d-x, but I cannot find a good tutorial for the 4.0 version.
For the context, I'm developping a top-down game for as a personnal project. The light would spread directly in front of the player like a flash-light, lighting every object in front of him and "colliding" with the walls and entities
The Shader hint
I've found multiple repos creating a custom light effect with cocos2d-x, but they all use the 3.0 version. The 4.0 version changed how the OpenGL code is managed in the Cocos backend.
Here's the repos I've found :
https://github.com/CodeAndWeb/cocos2d-x-dynamic-lighting
https://github.com/zerodarkzone/Cocos2d-x-lights
https://github.com/wantnon2/EffectNodes-for-cocos2dx
I've also read the Cocos2d-x Gitbook which explains what exactly changed in the CCSprite file.
Since I'm a beginner to Cocos2d-x and shader rendering, I did not understand what I had to change to make the old repos work ..
The polygon hint
I tried to create a VisionZone that would be a Cocos2d-x Node. The node would have a physic body colliding with the walls. On collision, the shape would update and adapt its shape.
Here's what the code would look like :
VisionZone.h
class VisionZone : public Node {
public:
static std::vector<Vec2> sm_shapeCollisionPoints;
static VisionZone *create(Vec2 origin, int visionDetail);
bool init() override;
void update(float delta) override;
void UpdateWithPlayer(Vec2 playerPos);
private:
void CreateNewPhysicBody(std::vector<Vec2> &points);
void UpdateShapeAndRedraw();
static bool IsInLine(Vec2 segmentStart, Vec2 segmentEnd, Vec2 point);
void CompareWithContactPoints();
void Rotate();
std::vector<Vec2> m_nonCollidedSegmentsEnds;
std::vector<Vec2> m_collidedSegmentsEnds;
Vec2 m_origin;
DrawNode *m_pVisionZoneDrawer;
int m_visionDetail;
VisionZone.cpp
std::vector<Vec2> VisionZone::sm_shapeCollisionPoints = {};
VisionZone *VisionZone::create(Vec2 origin, int visionDetail) {
auto *_obj = new(std::nothrow) VisionZone();
if (_obj && _obj->init()) {
_obj->m_origin = origin;
_obj->m_visionDetail = visionDetail;
int mid = std::floor(_obj->m_visionDetail * 0.5);
for (int i = -(mid); i <= mid; i++) {
_obj->m_nonCollidedSegmentsEnds.emplace_back(
static_cast<float>(_obj->m_origin.x + float(i) * 1.3f), _obj->m_origin.y + 300
);
}
_obj->m_collidedSegmentsEnds = _obj->m_nonCollidedSegmentsEnds;
std::vector<Vec2> _points = _obj->m_nonCollidedSegmentsEnds;
_points.emplace_back(_obj->m_origin);
_obj->CreateNewPhysicBody(_points);
} else
CC_SAFE_DELETE(_obj);
return _obj;
}
bool VisionZone::init() {
if (!Node::init()) return false;
m_pVisionZoneDrawer = DrawNode::create();
m_pVisionZoneDrawer->setPosition(m_origin);
assert(m_pVisionZoneDrawer);
addChild(m_pVisionZoneDrawer);
return true;
}
void VisionZone::update(float delta) {
Node::update(delta);
CompareWithContactPoints();
Rotate();
UpdateShapeAndRedraw();
}
void VisionZone::UpdateWithPlayer(Vec2 playerPos) {
m_origin = playerPos;
setPosition(m_origin);
}
void VisionZone::CreateNewPhysicBody(std::vector<Vec2> &points) {
points.push_back(m_origin);
PhysicsBody *_body = PhysicsBody::createEdgePolygon(
&points.front(),
int(points.size()),
PHYSICSBODY_MATERIAL_DEFAULT,
1
);
_body->setCategoryBitmask(vision_zone_collision_bitmask);
_body->setCollisionBitmask(map_collision_bitmask);
_body->setContactTestBitmask(true);
_body->setDynamic(false);
setPhysicsBody(_body);
}
bool VisionZone::IsInLine(Vec2 segmentStart, Vec2 segmentEnd, Vec2 point) {
float _startToPoint = sqrt(pow((segmentStart.x - point.x), 2) + pow((segmentStart.y - point.y), 2));
float _pointToEnd = sqrt(pow((point.x - segmentEnd.x), 2) + pow((point.y - segmentEnd.y), 2));
float _startToEnd = sqrt(pow((segmentStart.x - segmentEnd.x), 2) + pow((segmentStart.y - segmentEnd.y), 2));
return (_startToPoint + _pointToEnd == _startToEnd);
}
void VisionZone::CompareWithContactPoints() {
if (sm_shapeCollisionPoints.empty()) {
m_collidedSegmentsEnds = {m_origin, m_nonCollidedSegmentsEnds.front(), m_nonCollidedSegmentsEnds.back()};
return;
}
for (Vec2 &_nonCollidedEnd: m_nonCollidedSegmentsEnds) {
for (Vec2 &_contactPoint: sm_shapeCollisionPoints) {
if (IsInLine(m_origin, _nonCollidedEnd, _contactPoint)) {
Vec2 _midPoint = (m_nonCollidedSegmentsEnds.front() + m_nonCollidedSegmentsEnds.back()) / 2;
if (IsInLine(m_nonCollidedSegmentsEnds.front(), _midPoint, _contactPoint)) {
m_collidedSegmentsEnds = {
m_origin, sm_shapeCollisionPoints.front(), sm_shapeCollisionPoints.back(),
m_nonCollidedSegmentsEnds.back()
};
} else if (IsInLine(_midPoint, m_nonCollidedSegmentsEnds.back(), _contactPoint)) {
m_collidedSegmentsEnds = {
m_origin, m_nonCollidedSegmentsEnds.front(), sm_shapeCollisionPoints.front(),
sm_shapeCollisionPoints.back()
};
}
}
}
}
}
void VisionZone::Rotate() {
float _distanceX = InputManager::GetCursorPosX() - getPositionX();
float _distanceY = InputManager::GetCursorPosY() - getPositionY();
float _angle = atan2(_distanceY, _distanceX) * 180.f / static_cast<float>(M_PI);
setRotation(_angle);
}
void VisionZone::UpdateShapeAndRedraw() {
PhysicsShape *_newShape = getPhysicsBody()->addShape(PhysicsShapePolygon::create(
m_collidedSegmentsEnds.data(), int(m_collidedSegmentsEnds.size()))
);
assert(_newShape);
m_pVisionZoneDrawer->clear();
m_pVisionZoneDrawer->drawSolidPoly(
&m_collidedSegmentsEnds.front(),
m_collidedSegmentsEnds.size(),
Color4F(1.f, 0.94, 0.7, 1)
);
}
CollisionManager.cpp
#include "CollisionManager.h"
#include "Utility/Bitmasks.h"
USING_NS_CC;
EventListenerPhysicsContact *CollisionManager::m_contactListener = nullptr;
void CollisionManager::Init() {
m_contactListener = EventListenerPhysicsContact::create();
m_contactListener->onContactBegin = [](PhysicsContact &contact) { return ContactBeginCallback(contact); };
m_contactListener->onContactSeparate = [](PhysicsContact &contact) { return ContactSeparateCallback(contact); };
}
bool CollisionManager::ContactBeginCallback(PhysicsContact &contact) {
PhysicsBody *_bodyA = contact.getShapeA()->getBody();
PhysicsBody *_bodyB = contact.getShapeB()->getBody();
const bool visionAndWallCondition = ((_bodyA->getCategoryBitmask() == vision_zone_collision_bitmask &&
_bodyB->getCategoryBitmask() == map_collision_bitmask) ||
(_bodyB->getCategoryBitmask() == vision_zone_collision_bitmask &&
_bodyA->getCategoryBitmask() == map_collision_bitmask));
if (visionAndWallCondition) {
for (Vec2 _p: contact.getContactData()->points) VisionZone::sm_shapeCollisionPoints.push_back(_p);
return true;
}
return false;
}
bool CollisionManager::ContactSeparateCallback(PhysicsContact &contact) {
PhysicsBody *_bodyA = contact.getShapeA()->getBody();
PhysicsBody *_bodyB = contact.getShapeB()->getBody();
const bool visionAndWallCondition = ((_bodyA->getCategoryBitmask() == vision_zone_collision_bitmask &&
_bodyB->getCategoryBitmask() == map_collision_bitmask) ||
(_bodyB->getCategoryBitmask() == vision_zone_collision_bitmask &&
_bodyA->getCategoryBitmask() == map_collision_bitmask));
if (visionAndWallCondition) {
VisionZone::sm_shapeCollisionPoints.clear();
return true;
}
return false;
}
GameLayer.cpp
bool GameLayer::init() {
if (!Layer::init()) return false;
CollisionManager::Init();
_eventDispatcher->addEventListenerWithSceneGraphPriority(CollisionManager::GetContactListener(), this);
// ... SOME CODE ... //
m_visionZone = VisionZone::create(player->getPosition(), 100);
addChild(m_visionZone, 1);
return true;
}
void GameLayer::update(float delta) {
Node::update(delta);
m_visionZone->UpdateWithPlayer(m_player->getPosition());
}
I am not sure about this method though ... I feels like it is not optimal at all ?
Really curious to have some feedback on this, thanks in advance, sorry for the quantity of code :)
Let me know if you need more informations, code or links

My kinematic body position and scale are different than the expected position and scale

I'm trying to create a physics system for my "game engine" using Box2D, And when i create a b2_kinematicBody The position is bigger than the expected position and the scale is small than the expected scale.
I tried to change the SetAsBox parameters with SetAsBox(this->transform->scale.x, transform->scale.y) and the same problem
NOTE: For the rendering i'm using SDL2 and the positions are based in the SDL2 positions.
Example of the error
And here is a video of the problem: https://youtu.be/0fMPh8Rk_pY
This is the code of my KinematicBody2D class.
KinematicBody2D.h
class KinematicBody2D : public Component
{
private:
b2BodyDef k_bodydef;
b2Body* k_body;
b2FixtureDef k_fixtureDef;
b2Fixture *k_fixture;
b2World* k_world;
b2PolygonShape polygonShape;
public:
KinematicBody2D();
KinematicBody2D(Scene scene);
void Start() override;
void Loop(float delta) override;
void MovePosition(Vector2 target);
};
KinematicBody2D.cpp (Just the important part)
void KinematicBody2D::Start()
{
this->k_bodydef.type = b2BodyType::b2_kinematicBody;
this->k_bodydef.position = b2Vec2(this->transform->position.x, this->transform->position.y);
this->k_bodydef.angle = Math::Deg2Rad(this->transform->rotation);
this->k_body = this->k_world->CreateBody(&this->k_bodydef);
this->polygonShape.SetAsBox((this->transform->scale.x / 2.0f), (this->transform->scale.y / 2.0f));
this->k_fixtureDef.shape = &this->polygonShape;
this->k_fixture = this->k_body->CreateFixture(&this->k_fixtureDef);
}
void KinematicBody2D::Loop(float delta)
{
this->transform->position.x = this->k_body->GetPosition().x;
this->transform->position.y = this->k_body->GetPosition().y;
this->transform->rotation = Math::Rad2Deg(this->k_body->GetAngle());
this->polygonShape.SetAsBox((this->transform->scale.x / 2.0f), (this->transform->scale.y / 2.0f));
this->transform->scale = Vector2(this->transform->scale.x, this->transform->scale.y);
return;
}
main.cpp (Just the important part)
int main()
{
GameObject playerObj = GameObject("Player");
Scene scene = Scene("Scene", &playerObj);
playerObj.AddComponent<Sprite>("res/Player.png", Vector4(0, 0, 256, 256),
Vector2(64, 64));
playerObj.AddComponent<Rigidbody2D>(scene);
playerObj.AddComponent<Player>(app.GetRawRenderer());
GameObject floor = GameObject("Floor", "Floor");
floor.AddComponent<Sprite>("res/World 1.png");
floor.AddComponent<KinematicBody2D>(scene);
floor.transform.position = Vector2(100, 200);
}

World to screen space coordinates in OpenSceneGraph

So I've got a class Label that inherits from osg::Geode which I draw in the world space in OpenSceneGraph. After displaying each frame, I then want to read the screen space coordinates of
each Label, so I can find out how much they overlap in the screen space. To this end, I created a class ScreenSpace which should calculate this (the interesting function is calc_screen_coords.)
I wrote a small subroutine that dumps each frame with some extra information, including the ScreenSpace box which represents what the program thinks the screen space coordinates are:
Now in the above picture, there seems to be no problem; but if I rotate it to the other side (with my mouse), then it looks quite different:
And that is what I don't understand.
Is my world to screen space calculation wrong?
Or am I getting the wrong BoundingBox from the Drawable?
Or maybe it has something to do with the setAutoRotateToScreen(true) directive that I give the osgText::Text object?
Is there a better way to do this? Should I try to use a Billboard instead? How would I do that though? (I tried and it totally didn't work for me — I must be missing something...)
Here is the source code for calculating the screen space coordinates of a Label:
struct Pixel {
// elided methods...
int x;
int y;
}
// Forward declarations:
pair<Pixel, Pixel> calc_screen_coords(const osg::BoundingBox& box, const osg::Camera* cam);
void rearange(Pixel& left, Pixel& right);
class ScreenSpace {
public:
ScreenSpace(const Label* label, const osg::Camera* cam)
{
BoundingBox box = label->getDrawable(0)->computeBound();
tie(bottom_left_, upper_right_) = calc_screen_coords(box, cam);
rearrange(bottom_left_, upper_right_);
}
// elided methods...
private:
Pixel bottom_left_;
Pixel upper_right_;
}
pair<Pixel, Pixel> calc_screen_coords(const osg::BoundingBox& box, const osg::Camera* cam)
{
Vec4d vec (box.xMin(), box.yMin(), box.zMin(), 1.0);
Vec4d veq (box.xMax(), box.yMax(), box.zMax(), 1.0);
Matrixd transmat
= cam->getViewMatrix()
* cam->getProjectionMatrix()
* cam->getViewport()->computeWindowMatrix();
vec = vec * transmat;
vec = vec / vec.w();
veq = veq * transmat;
veq = veq / veq.w();
return make_pair(
Pixel(static_cast<int>(vec.x()), static_cast<int>(vec.y())),
Pixel(static_cast<int>(veq.x()), static_cast<int>(veq.y()))
);
}
inline void swap(int& v, int& w)
{
int temp = v;
v = w;
w = temp;
}
inline void rearrange(Pixel& left, Pixel& right)
{
if (left.x > right.x) {
swap(left.x, right.x);
}
if (left.y > right.y) {
swap(left.y, right.y);
}
}
And here is the construction of Label (I tried to abridge it a little):
// Forward declaration:
Geometry* createLeader(straph::Point pos, double height, Color color);
class Label : public osg::Geode {
public:
Label(font, fontSize, text, color, position, height, margin, bgcolor, leaderColor)
{
osgText::Text* txt = new osgText::Text;
txt->setFont(font);
txt->setColor(color.vec4());
txt->setCharacterSize(fontSize);
txt->setText(text);
// Set display properties and height
txt->setAlignment(osgText::TextBase::CENTER_BOTTOM);
txt->setAutoRotateToScreen(true);
txt->setPosition(toVec3(position, height));
// Create bounding box and leader
typedef osgText::TextBase::DrawModeMask DMM;
unsigned drawMode = DMM::TEXT | DMM::BOUNDINGBOX;
drawMode |= DMM::FILLEDBOUNDINGBOX;
txt->setBoundingBoxColor(bgcolor.vec4());
txt->setBoundingBoxMargin(margin);
txt->setDrawMode(drawMode);
this->addDrawable(txt);
Geometry* leader = createLeader(position, height, leaderColor);
this->addDrawable(leader);
}
// elided methods and data members...
}
Geometry* createLeader(straph::Point pos, double height, Color color)
{
Geometry* leader = new Geometry();
Vec3Array* array = new Vec3Array();
array->push_back(Vec3(pos.x, pos.y, height));
array->push_back(Vec3(pos.x, pos.y, 0.0f));
Vec4Array* colors = new Vec4Array(1);
(*colors)[0] = color.vec4();
leader->setColorArray(colors);
leader->setColorBinding(Geometry::BIND_OVERALL);
leader->setVertexArray(array);
leader->addPrimitiveSet(new DrawArrays(PrimitiveSet::LINES, 0, 2));
LineWidth* lineWidth = new osg::LineWidth();
lineWidth->setWidth(2.0f);
leader->getOrCreateStateSet()->setAttributeAndModes(lineWidth, osg::StateAttribute::ON);
return leader;
}
Any pointers or help?
I found a solution that works for me, but is also unsatisfying, so if you have a better solution, I'm all ears.
Basically, I take different points from the Label that I know will be at certain points,
and I calculate the screen space by combining this. For the left and right sides, I take
the bounds of the regular bounding box, and for the top and bottom, I calculate it with the
center of the bounding box and the position of the label.
ScreenSpace::ScreenSpace(const Label* label, const osg::Camera* cam)
{
const Matrixd transmat
= cam->getViewMatrix()
* cam->getProjectionMatrix()
* cam->getViewport()->computeWindowMatrix();
auto topixel = [&](Vec3 v) -> Pixel {
Vec4 vec(v.x(), v.y(), v.z(), 1.0);
vec = vec * transmat;
vec = vec / vec.w();
return Pixel(static_cast<int>(vec.x()), static_cast<int>(vec.y()));
};
// Get left right coordinates
vector<int> xs; xs.reserve(8);
vector<int> ys; ys.reserve(8);
BoundingBox box = label->getDrawable(0)->computeBound();
for (int i=0; i < 8; i++) {
Pixel p = topixel(box.corner(i));
xs.push_back(p.x);
ys.push_back(p.y);
};
int xmin = *min_element(xs.begin(), xs.end());
int xmax = *max_element(xs.begin(), xs.end());
// Get up-down coordinates
int ymin = topixel(dynamic_cast<const osgText::Text*>(label->getDrawable(0))->getPosition()).y;
int center = topixel(box.center()).y;
int ymax = center + (center - ymin);
bottom_left_ = Pixel(xmin, ymin);
upper_right_ = Pixel(xmax, ymax);
z_ = distance_from_camera(label, cam);
}

How to convert mouse coordinate on screen to 3D coordinate

I'm creating a 3D application using GLUT in C++.
Now, I want to implement a method similar to this:
Vector3* MyClass::get3DObjectfromMouse(int mouseX, int mouseY);
How can I implement this method?
As it was commented by Andon M. Coleman, one way you can achieve this is by doing a ray/object intersection test, with unprojected screen coordinates. This technique is commonly known as picking.
A pseudo-C++ code for picking:
Assume we have a 3D object type/class:
class Object3D { ... };
A 3D picking function would return a list of all objects that are intersected by a line going from the given 2D point in the near plane to the same point in the far plane.
struct LineSegment
{
Vector3 start;
Vector3 end;
};
Object3D[] Pick(float x, float y)
{
LineSegment lineSeg;
Object3D[] intersectedObjs;
// Do both un-projections for z-near (0) and z-far (1).
// This produces a line segment going from z-near to far.
UnProject(x, y, /* z = */ 0.0, modelViewMatrix, projectionMatrix, viewport, lineSeg.start);
UnProject(x, y, /* z = */ 1.0, modelViewMatrix, projectionMatrix, viewport, lineSeg.end);
// Iterate all object in the scene or in the current view:
for (Object3D obj : scene)
{
if (TestLineIntersection(obj, lineSeg))
{
// This object is crossed by the picking line.
intersectedObjs.Add(obj);
}
}
// Optionally you might want sort them from distance
// to the camera/viewer before returning the intersections.
return intersectedObjs;
}
And the UnProject() function would look like this:
bool UnProject(float winX, float winY, float winZ,
const Matrix4 & modelView, const Matrix4 & projection,
const ScreenRect viewport, Vector3 & worldCoordinates)
{
// Compute (projection x modelView) ^ -1:
const Matrix4 m = inverse(projection * modelView);
// Need to invert Y since screen Y-origin point down,
// while 3D Y-origin points up (this is an OpenGL only requirement):
winY = viewport.Height() - winY;
// Transformation of normalized coordinates between -1 and 1:
Vector4 in;
in[0] = (winX - viewport.X()) / viewport.Width() * 2.0 - 1.0;
in[1] = (winY - viewport.Y()) / viewport.Height() * 2.0 - 1.0;
in[2] = 2.0 * winZ - 1.0;
in[3] = 1.0;
// To world coordinates:
Vector4 out(m * in);
if (out[3] == 0.0) // Avoid a division by zero
{
worldCoordinates = Vector3Zero;
return false;
}
out[3] = 1.0 / out[3];
worldCoordinates[0] = out[0] * out[3];
worldCoordinates[1] = out[1] * out[3];
worldCoordinates[2] = out[2] * out[3];
return true;
}
To clarify, TestLineIntersection() does a line vs AABB intersection test. The bounding box should be transformed to world-space, since it is usually expressed as a set of points in local model-space.
bool TestLineIntersection(const Object3D & obj, const LineSegment & lineSeg)
{
AABB aabb = obj.GetAABB();
aabb.TransformBy(obj.modelMatrix);
return aabb.LineIntersection(lineSeg.start, lineSeg.end);
}
// AABB.cpp:
bool AABB::LineIntersection(const Vector3 & start, const Vector3 & end) const
{
const Vector3 center = (mins + maxs) * 0.5;
const Vector3 extents = maxs - center;
const Vector3 lineDir = 0.5 * (end - start);
const Vector3 lineCenter = start + lineDir;
const Vector3 dir = lineCenter - center;
const float ld0 = Mathf::Abs(lineDir[0]);
if (Mathf::Abs(dir[0]) > (extents[0] + ld0))
{
return false;
}
const float ld1 = Mathf::Abs(lineDir[1]);
if (Mathf::Abs(dir[1]) > (extents[1] + ld1))
{
return false;
}
const float ld2 = Mathf::Abs(lineDir[2]);
if (Mathf::Abs(dir[2]) > (extents[2] + ld2))
{
return false;
}
const Vector3 vCross = cross(lineDir, dir);
if (Mathf::Abs(vCross[0]) > (extents[1] * ld2 + extents[2] * ld1))
{
return false;
}
if (Mathf::Abs(vCross[1]) > (extents[0] * ld2 + extents[2] * ld0))
{
return false;
}
if (Mathf::Abs(vCross[2]) > (extents[0] * ld1 + extents[1] * ld0))
{
return false;
}
return true;
}

How do I draw lines using XNA?

I've read a bunch of tutorials involving XNA (and it's various versions) and I still am a little confused on drawing primitives. Everything seems to be really convoluted.
Can someone show me, using code, the simplest XNA implementation of drawing one or two lines on to the screen? Perhaps with a brief explanation (including the boilerplate)?
I'm not a games programmer and I have little XNA experience. My ultimate goal is to draw some lines onto the screen which I will eventually transform with rotations, etc (by hand). However, for this first step.. I need to simply draw the lines! I remember back in my ancient OpenGL days it was fairly straightforward when drawing a line with a few method calls. Should I simply revert to using unmanaged directx calls?
When working with XNA, everything (even 2d primitives) have to be expressed in a way that a 3d card can understand, which means that a line is just a set of vertices.
MSDN has a pretty good walkthrough here:
http://msdn.microsoft.com/en-us/library/bb196414.aspx#ID2EEF
You'll find that it takes more code to render a primitive line than it would take to just setup a textured quad and rotate that, since in essence, your doing the same thing when rendering a line.
Following NoHayProblema's answer (I cannot comment yet).
That answer, although the correct one for this old question, is incomplete. Texture2D constructor returns an uninitialized texture, which is never painted on screen.
In order to use that approach, you need to set the texture's data like this:
Texture2D SimpleTexture = new Texture2D(GraphicsDevice, 1, 1, false,
SurfaceFormat.Color);
Int32[] pixel = {0xFFFFFF}; // White. 0xFF is Red, 0xFF0000 is Blue
SimpleTexture.SetData<Int32> (pixel, 0, SimpleTexture.Width * SimpleTexture.Height);
// Paint a 100x1 line starting at 20, 50
this.spriteBatch.Draw(SimpleTexture, new Rectangle(20, 50, 100, 1), Color.Blue);
Take into account that the way you write the data into pixel must be consistent with the texture's SurfaceFormat. The example works because the texture is being formatted as RGB.
Rotations can be applied in spriteBatch.Draw like this:
this.spriteBatch.Draw (SimpleTexture, new Rectangle(0, 0, 100, 1), null,
Color.Blue, -(float)Math.PI/4, new Vector2 (0f, 0f), SpriteEffects.None, 1f);
found a tutorial for that
http://www.bit-101.com/blog/?p=2832
its using a BasicEffect (shader)
and the built in draw user primitive in XNA 4.0
some code samples i find helpful:
load content method
basicEffect = new BasicEffect(GraphicsDevice);
basicEffect.VertexColorEnabled = true;
basicEffect.Projection = Matrix.CreateOrthographicOffCenter
(0, GraphicsDevice.Viewport.Width,     // left, right
GraphicsDevice.Viewport.Height, 0,    // bottom, top
0, 1);   
draw method
basicEffect.CurrentTechnique.Passes[0].Apply();
var vertices = new VertexPositionColor[4];
vertices[0].Position = new Vector3(100, 100, 0);
vertices[0].Color = Color.Black;
vertices[1].Position = new Vector3(200, 100, 0);
vertices[1].Color = Color.Red;
vertices[2].Position = new Vector3(200, 200, 0);
vertices[2].Color = Color.Black;
vertices[3].Position = new Vector3(100, 200, 0);
vertices[3].Color = Color.Red;
GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineList, vertices, 0, 2);
have fun and vote up if this helped you. also pay a visit to the tutorial i got this from.
Well, you can do it in a very simple way without getting into the 3D horrible vector stuff.
Just create a quick texture, for example:
Texture2D SimpleTexture = new Texture2D(GraphicsDevice, 1, 1, false, SurfaceFormat.Color);
And then just draw a line using that texture:
this.spriteBatch.Draw(SimpleTexture, new Rectangle(100, 100, 100, 1), Color.Blue);
I hope this helps
The simplest best way, I think, is to get the image of just a white pixel then stretch that pixel in a rectangle to look like a line
I made a Line class,
class Line
{
Texture pixel = ((set this to a texture of a white pixel with no border));
Vector2 p1, p2; //this will be the position in the center of the line
int length, thickness; //length and thickness of the line, or width and height of rectangle
Rectangle rect; //where the line will be drawn
float rotation; // rotation of the line, with axis at the center of the line
Color color;
//p1 and p2 are the two end points of the line
public Line(Vector2 p1, Vector2 p2, int thickness, Color color)
{
this.p1 = p1;
this.p2 = p2;
this.thickness = thickness;
this.color = color;
}
public void Update(GameTime gameTime)
{
length = (int)Vector2.Distance(p1, p2); //gets distance between the points
rotation = getRotation(p1.X, p1.Y, p2.X, p2.Y); //gets angle between points(method on bottom)
rect = new Rectangle((int)p1.X, (int)p1.Y, length, thickness)
//To change the line just change the positions of p1 and p2
}
public void Draw(SpriteBatch spriteBatch, GameTime gameTime)
{
spriteBatch.Draw(pixel, rect, null, color, rotation, new Vector2.Zero, SpriteEffects.None, 0.0f);
}
//this returns the angle between two points in radians
private float getRotation(float x, float y, float x2, float y2)
{
float adj = x - x2;
float opp = y - y2;
float tan = opp / adj;
float res = MathHelper.ToDegrees((float)Math.Atan2(opp, adj));
res = (res - 180) % 360;
if (res < 0) { res += 360; }
res = MathHelper.ToRadians(res);
return res;
}
Hope this helps
There is also the "round line" code that "manders" has released on CodePlex:
http://roundline.codeplex.com/
Here is the blog post about it:
XNA RoundLine Code Released on CodePlex
Just stretch a white pixel.
point = game.Content.Load<Texture2D>("ui/point");
public void DrawLine(Vector2 start, Vector2 end, Color color)
{
Vector2 edge = end - start;
float angle = (float)Math.Atan2(edge.Y, edge.X);
spriteBatch.Begin();
spriteBatch.Draw(point,
new Rectangle((int)start.X, (int)start.Y, (int)edge.Length(), 1),
null,
color,
angle,
new Vector2(0, 0),
SpriteEffects.None,
0);
spriteBatch.End();
}
I wanted to draw rays so that I could debug rays created by explosions and where they intersect objects. This will draw a single pixel thin line between two points. This is what I did:
Class to store some simple ray data. The XNA default ray class could work, but it doesn't store the length of the ray to intersection.
public class myRay
{
public Vector3 position, direction;
public float length;
}
A list to store the rays that are to be drawn:
List<myRay> DebugRays= new List<myRay>();
Create a BasicEffect and pass it a "Matrix.CreateOrthographicOffCenter" projection with your desired resolution in the LoadContent method.
Then run this in the draw method:
private void DrawRays()
{
spriteBatch.Begin();
foreach (myRay ray in DebugRays)
{
//An array of 2 vertices - a start and end position
VertexPositionColor[] Vertices = new VertexPositionColor[2];
int[] Indices = new int[2];
//Starting position of the ray
Vertices[0] = new VertexPositionColor()
{
Color = Color.Orange,
Position = ray.position
};
//End point of the ray
Vertices[1] = new VertexPositionColor()
{
Color = Color.Orange,
Position = ray.position + (ray.direction * ray.length)
};
Indices[0] = 0;
Indices[1] = 1;
foreach (EffectPass pass in BasicEffect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.LineStrip, Vertices, 0, 2, Indices, 0, 1, VertexPositionColorTexture.VertexDeclaration);
}
}
spriteBatch.End();
}
So when an explosion happens in my game it does this (Psuedocode):
OnExplosionHappened()
{
DebugRays.Clear()
myRay ray = new myRay()
{
position = explosion.Position,
direction = GetDirection(explosion, solid),
//Used GetValueOrDefault here to prevent null value errors
length = explosionRay.Intersects(solid.BoundingBox).GetValueOrDefault()
};
DebugRays.Add(ray);
}
It's pretty simple (It possibly looks way more complicated than it is) and it'd be easy to put it into a separate class that you never have to think about again. It also lets you draw a whole lot of lines at once.
I encountered this problem my self and decided to make a class called LineBatch.
LineBatch will draw lines without needing a spriteBatch or dots.
The class is below.
public class LineBatch
{
bool cares_about_begin_without_end;
bool began;
GraphicsDevice GraphicsDevice;
List<VertexPositionColor> verticies = new List<VertexPositionColor>();
BasicEffect effect;
public LineBatch(GraphicsDevice graphics)
{
GraphicsDevice = graphics;
effect = new BasicEffect(GraphicsDevice);
Matrix world = Matrix.Identity;
Matrix view = Matrix.CreateTranslation(-GraphicsDevice.Viewport.Width / 2, -GraphicsDevice.Viewport.Height / 2, 0);
Matrix projection = Matrix.CreateOrthographic(GraphicsDevice.Viewport.Width, -GraphicsDevice.Viewport.Height, -10, 10);
effect.World = world;
effect.View = view;
effect.VertexColorEnabled = true;
effect.Projection = projection;
effect.DiffuseColor = Color.White.ToVector3();
cares_about_begin_without_end = true;
}
public LineBatch(GraphicsDevice graphics, bool cares_about_begin_without_end)
{
this.cares_about_begin_without_end = cares_about_begin_without_end;
GraphicsDevice = graphics;
effect = new BasicEffect(GraphicsDevice);
Matrix world = Matrix.Identity;
Matrix view = Matrix.CreateTranslation(-GraphicsDevice.Viewport.Width / 2, -GraphicsDevice.Viewport.Height / 2, 0);
Matrix projection = Matrix.CreateOrthographic(GraphicsDevice.Viewport.Width, -GraphicsDevice.Viewport.Height, -10, 10);
effect.World = world;
effect.View = view;
effect.VertexColorEnabled = true;
effect.Projection = projection;
effect.DiffuseColor = Color.White.ToVector3();
}
public void DrawAngledLineWithRadians(Vector2 start, float length, float radians, Color color)
{
Vector2 offset = new Vector2(
(float)Math.Sin(radians) * length, //x
-(float)Math.Cos(radians) * length //y
);
Draw(start, start + offset, color);
}
public void DrawOutLineOfRectangle(Rectangle rectangle, Color color)
{
Draw(new Vector2(rectangle.X, rectangle.Y), new Vector2(rectangle.X + rectangle.Width, rectangle.Y), color);
Draw(new Vector2(rectangle.X, rectangle.Y), new Vector2(rectangle.X, rectangle.Y + rectangle.Height), color);
Draw(new Vector2(rectangle.X + rectangle.Width, rectangle.Y), new Vector2(rectangle.X + rectangle.Width, rectangle.Y + rectangle.Height), color);
Draw(new Vector2(rectangle.X, rectangle.Y + rectangle.Height), new Vector2(rectangle.X + rectangle.Width, rectangle.Y + rectangle.Height), color);
}
public void DrawOutLineOfTriangle(Vector2 point_1, Vector2 point_2, Vector2 point_3, Color color)
{
Draw(point_1, point_2, color);
Draw(point_1, point_3, color);
Draw(point_2, point_3, color);
}
float GetRadians(float angleDegrees)
{
return angleDegrees * ((float)Math.PI) / 180.0f;
}
public void DrawAngledLine(Vector2 start, float length, float angleDegrees, Color color)
{
DrawAngledLineWithRadians(start, length, GetRadians(angleDegrees), color);
}
public void Draw(Vector2 start, Vector2 end, Color color)
{
verticies.Add(new VertexPositionColor(new Vector3(start, 0f), color));
verticies.Add(new VertexPositionColor(new Vector3(end, 0f), color));
}
public void Draw(Vector3 start, Vector3 end, Color color)
{
verticies.Add(new VertexPositionColor(start, color));
verticies.Add(new VertexPositionColor(end, color));
}
public void End()
{
if (!began)
if (cares_about_begin_without_end)
throw new ArgumentException("Please add begin before end!");
else
Begin();
if (verticies.Count > 0)
{
VertexBuffer vb = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColor), verticies.Count, BufferUsage.WriteOnly);
vb.SetData<VertexPositionColor>(verticies.ToArray());
GraphicsDevice.SetVertexBuffer(vb);
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.DrawPrimitives(PrimitiveType.LineList, 0, verticies.Count / 2);
}
}
began = false;
}
public void Begin()
{
if (began)
if (cares_about_begin_without_end)
throw new ArgumentException("You forgot end.");
else
End();
verticies.Clear();
began = true;
}
}
Here is a simple way that I use to make lines by specifying a start coordinate, an end coordinate, width, and color of them:
NOTE: you must add a file named "dot" to the content directory (the line will be made out of these).
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace Xna.LineHelper
{
public class LineManager
{
int loopCounter;
int lineLegnth;
Vector2 lineDirection;
Vector2 _position;
Color dotColor;
Rectangle _rectangle;
List<Texture2D> _dots = new List<Texture2D>();
FunctionsLibrary functions = new FunctionsLibrary();
public void CreateLineFiles(Vector2 startPosition, Vector2 endPosition, int width, Color color, ContentManager content)
{
dotColor = color;
_position.X = startPosition.X;
_position.Y = startPosition.Y;
lineLegnth = functions.Distance((int)startPosition.X, (int)endPosition.X, (int)startPosition.Y, (int)endPosition.Y);
lineDirection = new Vector2((endPosition.X - startPosition.X) / lineLegnth, (endPosition.Y - startPosition.Y) / lineLegnth);
_dots.Clear();
loopCounter = 0;
_rectangle = new Rectangle((int)startPosition.X, (int)startPosition.Y, width, width);
while (loopCounter < lineLegnth)
{
Texture2D dot = content.Load<Texture2D>("dot");
_dots.Add(dot);
loopCounter += 1;
}
}
public void DrawLoadedLine(SpriteBatch sb)
{
foreach (Texture2D dot in _dots)
{
_position.X += lineDirection.X;
_position.Y += lineDirection.Y;
_rectangle.X = (int)_position.X;
_rectangle.Y = (int)_position.Y;
sb.Draw(dot, _rectangle, dotColor);
}
}
}
public class FunctionsLibrary
{
//Random for all methods
Random Rand = new Random();
#region math
public int TriangleArea1(int bottom, int height)
{
int answer = (bottom * height / 2);
return answer;
}
public double TriangleArea2(int A, int B, int C)
{
int s = ((A + B + C) / 2);
double answer = (Math.Sqrt(s * (s - A) * (s - B) * (s - C)));
return answer;
}
public int RectangleArea(int side1, int side2)
{
int answer = (side1 * side2);
return answer;
}
public int SquareArea(int side)
{
int answer = (side * side);
return answer;
}
public double CircleArea(int diameter)
{
double answer = (((diameter / 2) * (diameter / 2)) * Math.PI);
return answer;
}
public int Diference(int A, int B)
{
int distance = Math.Abs(A - B);
return distance;
}
#endregion
#region standardFunctions
public int Distance(int x1, int x2, int y1, int y2)
{
return (int)(Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)));
}
#endregion
}
}