Page 1 of 3

Beam scenenode

Posted: Thu Nov 17, 2005 9:40 pm
by cdbarrett
I got bored today and created this.
Draws 2 quads from point a to point b in a cross pattern.

Image

The node

Code: Select all

// Just a simple wrapper :D
struct IrrQuad {
	video::S3DVertex verts[4];
};

class CBeamNode : public scene::ISceneNode {
	private:
		// The beam material.
		video::SMaterial material;

		// Start/End Points
		core::vector3df vStart;
		core::vector3df vEnd;

		// Bounding Box
		core::aabbox3d<f32> Box;

		// Size of the beam
		float flScale;

		// Beam color
		video::SColor beamColor;

		void DrawQuad( IrrQuad& quad ) {
			u16 indices[] = { 0,2,3, 2,1,3, 1,0,3, 2,0,1 };
			video::IVideoDriver* driver = SceneManager->getVideoDriver();
			driver->setMaterial(material);
			driver->drawIndexedTriangleList( &quad.verts[0], 4, &indices[0], 4 );
		}

		// Thanks to whoever wrote this little function :)
		core::vector3df getTargetAngle( core::vector3df v, core::vector3df r) {
			//v -current node position
			//r -target node position

			core::vector3df angle;
			float x,y,z;
			x = r.X - v.X;
			y = r.Y - v.Y;
			z = r.Z - v.Z;

			//angle in X-Z plane
			angle.Y = atan2 (x, z);
			angle.Y *= (180 / 3.14); //converting from rad to degrees

			//just making sure angle is somewhere between 0-360 degrees
			if(angle.Y < 0) angle.Y += 360;
			if(angle.Y >= 360) angle.Y -= 360;

			//angle in Y-Z plane while Z and X axes are already rotated around Y
			float z1 = sqrt(x*x + z*z);

			angle.X = atan2 (z1, y);
			angle.X *= (180 / 3.14); //converting from rad to degrees
			angle.X -= 90;

			//just making sure angle is somewhere between 0-360 degrees
			if(angle.X < 0) angle.X += 360;
			if(angle.X >= 360) angle.X -= 360;

			return angle;
		} 

	public:

		CBeamNode( scene::ISceneNode* parent, scene::ISceneManager *mgr, s32 id, char* szBeam ) : scene::ISceneNode( parent, mgr, id ) {
			// Setup the beam material
			material.Wireframe = false;
			material.Lighting = false;
			material.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
			material.Texture1 = mgr->getVideoDriver( )->getTexture( szBeam );

			// Default to 32 units for the scale
			flScale = 32.0;

			// Default to white
			beamColor.set( 255, 255, 255, 255 );
		}

		virtual void OnPreRender( ) {
			if( IsVisible ) {
				SceneManager->registerNodeForRendering( this );
			}
		}

		virtual void render( ) {
			// Figure out quads based on start/end points.
			core::matrix4 m;
			m.setRotationDegrees( getTargetAngle( vStart, vEnd ) );
			core::vector3df vUp( 0, 1, 0 );
			core::vector3df vRight( -1, 0, 0 );
			m.transformVect( vRight );
			m.transformVect( vUp );

			// Draw the first cross
			IrrQuad beam;
			beam.verts[0] = video::S3DVertex( vStart + vUp * flScale, core::vector3df( 1, 1, 0 ), beamColor, core::vector2df( 0, 1 ) );
			beam.verts[1] = video::S3DVertex( vStart + vUp * -flScale, core::vector3df( 1, 0, 0 ), beamColor, core::vector2df( 1, 1 ) );
			beam.verts[2] = video::S3DVertex( vEnd + vUp * -flScale, core::vector3df( 0, 1, 1 ), beamColor, core::vector2df( 1, 0 ) );
			beam.verts[3] = video::S3DVertex( vEnd + vUp * flScale, core::vector3df( 0, 0, 1 ), beamColor, core::vector2df( 0, 0 ) );
			DrawQuad( beam );

			// Draw the second cross.
			beam.verts[0] = video::S3DVertex( vStart + vRight * flScale, core::vector3df( 1, 1, 0 ), beamColor, core::vector2df( 0, 1 ) );
			beam.verts[1] = video::S3DVertex( vStart + vRight * -flScale, core::vector3df( 1, 0, 0 ), beamColor, core::vector2df( 1, 1 ) );
			beam.verts[2] = video::S3DVertex( vEnd + vRight * -flScale, core::vector3df( 0, 1, 1 ), beamColor, core::vector2df( 1, 0 ) );
			beam.verts[3] = video::S3DVertex( vEnd + vRight * flScale, core::vector3df( 0, 0, 1 ), beamColor, core::vector2df( 0, 0 ) );
			DrawQuad( beam );
		}

		virtual const core::aabbox3d<f32>& getBoundingBox() const {
			return Box;
		}

		virtual s32 getMaterialCount() {
			return 1;
		}

		virtual video::SMaterial& getMaterial(s32 i) {
			return material;
		} 

		void SetStartPoint( core::vector3df pos ) {
			vStart = pos;
		}

		void SetEndPoint( core::vector3df pos ) {
			vEnd = pos;
		}

		void SetBeamScale( float size ) {
			flScale = size;
		}

		void SetBeamColor( video::SColor color ) {
			beamColor = color;
		}
};
How to use.

Code: Select all

CBeamNode* beam = new CBeamNode( smgr->getRootSceneNode( ), smgr, 200, "laserbeam.bmp" );
beam->SetStartPoint( core::vector3df( -500, 0, 0 ) );
beam->SetEndPoint( core::vector3df( 500, 0, 0 ) );
beam->SetBeamColor( video::SColor( 255, 0, 255, 0 ) );
beam->drop( );
That creates a green laser beam using the given texture.

Posted: Thu Nov 17, 2005 9:55 pm
by dracflamloc
Thats very handy thank you!

Posted: Sat Nov 19, 2005 1:17 am
by Guest
Noob question-sorry.

Where should

Code: Select all

 CBeamNode* beam = new CBeamNode( smgr->getRootSceneNode( ), smgr, 200, "laserbeam.bmp" );
beam->SetStartPoint( core::vector3df( -500, 0, 0 ) );
beam->SetEndPoint( core::vector3df( 500, 0, 0 ) );
beam->SetBeamColor( video::SColor( 255, 0, 255, 0 ) );
beam->drop( );
go?

thanks

Posted: Sat Nov 19, 2005 1:56 am
by sRc
Anonymous wrote:Noob question-sorry.

Where should

Code: Select all

 CBeamNode* beam = new CBeamNode( smgr->getRootSceneNode( ), smgr, 200, "laserbeam.bmp" );
beam->SetStartPoint( core::vector3df( -500, 0, 0 ) );
beam->SetEndPoint( core::vector3df( 500, 0, 0 ) );
beam->SetBeamColor( video::SColor( 255, 0, 255, 0 ) );
beam->drop( );
go?

thanks
the first 4 lines when you create it, and the drop() when you decide to get rid of it :)

Posted: Sat Nov 19, 2005 4:43 am
by Guest
Ok thanks. I know where my problem was now. The line was drawn. It was just to small to see, maybe lol. I just put beam->SetBeamScale(0.5); and it worked :) nice work man

Posted: Sat Nov 19, 2005 3:38 pm
by bitplane
cool
very useful thank you :)

Posted: Sat Nov 19, 2005 4:39 pm
by Saku
Just a little proposition; why not have the StartPoint, EndPoint and BeamColor as parameters in the CBeam from the start instead of having to call those functions later?
Seems more convinient ..well to me anyways ;)

Posted: Sun Dec 18, 2005 6:49 pm
by Midnight
what given texture??

Posted: Sun Dec 18, 2005 8:14 pm
by afecelis
Nice work CdBarrett!

you could also upload your test app for us to try or see it in action immediately.

If you need someone to mirror it I could give you a hand with that. :wink:

Posted: Wed Dec 21, 2005 9:32 am
by Guest
Alternative suggestion (as the above looks like a lot of code to achieve this, but maybe it's just because other people tend to use comments).

You want a beam, so the widest side of the quad should be facing the camera. How to achieve that? Simple:

It is assumed that you build the beam starting at local origin and stretching along z. The texture is facing along y (considered "up"). The beam is just a a quad with corners
(width, 0, 0)
(-width, 0, 0)
(-width, 0, (end-begin).length)
(width, 0, (end-begin).length)

matrix mat={x,x,x,x, x,x,x,x, (end-begin).normalize, begin}
Meaning, the right and up vector of the matrix don't matter, the will be rebuild when rendering anyway. Forward is the direction of the beam, position is the starting point.

You have start and end point for the beam, that will be your z axis for the transformation. But we still need two more vectors. Actually just one, the third is the cross product.

Code: Select all

   vector3df zdir = end-begin;
   zdir.normalize();
   vector3df beamToCam = activeCamera->getAbsolutePosition().normalize();
//That's the important part. We want the "right" vector of the beam to be //perpendicular to the vector to the cam, so it will face as with maximum width.
   vector3df right = beamToCam.crossProduct(zdir);
   vector3df up = right.crossProduct(zdir);

   //No, you can't do that, but I would strongly suggest adding a constructor to allow exactly that instead of setting everything manually
   matrix4 mat(right.X, right.Y, right.Z, 0, 
                     up.X, up.Y, up.Z, 0,
                     zdir.X, zdir.Y, zdir.Z, 0,
                     position.X, position.Y, position.Z,1);
   setRotation(mat.getRotationDegrees());
Essentially, making a quad face the cam but retaining one of its axes is a matter of two cross products. Of course, if it is still moving directly towards are away from the cam it will still disappear. But you can always add two small quads with a circular texture as caps. You always build that thing like you model any object.. in local space.

The only thing to show how it would look is this:
http://festini.device-zero.de/Programmi ... ads/px.zip

It's old, and not really a game. Just some 50 stupid AI spaceships wildly shooting at each other. W,A,S,D to rotate, tab to accelerate, shift to slow down, strg/cntrl to shoot, t to target next ship. If you quickly turn around you should see enough of these beams to judge the effect.

Posted: Thu Feb 02, 2006 6:29 pm
by Mike
Shouldn't there be something like:

Code: Select all

SceneManager->getVideoDriver()->setTransform(irr::video::ETS_WORLD, AbsoluteTransformation); 
in the beginning of render()?
I had some strange issues without that line.

Posted: Mon May 08, 2006 7:43 am
by theteapot
I have a problem that if I go too close to the beam (or try to look alongside it), it suddenly disappears. Also, where is "laserbeam.bmp"?

CBeamNode* beam = new CBeamNode( smgr->getRootSceneNode( ), smgr, 200, "laserbeam.bmp" );
beam->SetStartPoint( core::vector3df( -500, 0, 0 ) );
beam->SetEndPoint( core::vector3df( 500, 0, 0 ) );
beam->SetBeamColor( video::SColor( 128, 0, 255, 0 ) );
beam->SetBeamScale(1.0);
beam->drop( );

Posted: Mon May 08, 2006 7:50 am
by jam
theteapot wrote:Also, where is "laserbeam.bmp"?
You can just create your own texture for the beam.

Posted: Wed Jun 04, 2008 2:45 am
by doqkhanh
Better in Irrlicht 1.4 with 1 line changed:

Code: Select all

      
         //...
         material.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
		 material.setTexture(0, mgr->getVideoDriver( )->getTexture( szBeam ));

         // Default to 32 units for the scale
         //....

Posted: Wed Jul 16, 2008 3:36 am
by empirepro
How can I get this to shoot from my ship instead of a set location?