示例#1
0
//! renders the node.
void COctTreeSceneNode::render()
{
	video::IVideoDriver* driver = SceneManager->getVideoDriver();

	if (vertexType == -1 || !driver)
		return;

	ICameraSceneNode* camera = SceneManager->getActiveCamera();
	if (!camera)
		return;

	bool isTransparentPass =
		SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT;
	++PassCount;

	driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);

	SViewFrustum frust = *camera->getViewFrustum();

	//transform the frustum to the current absolute transformation
	core::matrix4 invTrans(AbsoluteTransformation);
	invTrans.makeInverse();
	/*
	//frust.transform(invTrans);
	//const core::aabbox3d<float> &box = frust.getBoundingBox();
	*/

	frust.transform(invTrans);

	switch(vertexType)
	{
	case video::EVT_STANDARD:
		{
			//StdOctTree->calculatePolys(box);
			StdOctTree->calculatePolys(frust);

			OctTree<video::S3DVertex>::SIndexData* d =  StdOctTree->getIndexData();

			for (u32 i=0; i<Materials.size(); ++i)
			{
				if ( 0 == d[i].CurrentSize )
					continue;

				video::IMaterialRenderer* rnd = driver->getMaterialRenderer(Materials[i].MaterialType);
				bool transparent = (rnd && rnd->isTransparent());

				// only render transparent buffer if this is the transparent render pass
				// and solid only in solid pass
				if (transparent == isTransparentPass)
				{
					driver->setMaterial(Materials[i]);
					driver->drawIndexedTriangleList(
						&StdMeshes[i].Vertices[0], StdMeshes[i].Vertices.size(),
						d[i].Indices, d[i].CurrentSize / 3);
				}
			}

			// for debug purposes only
			if ( DebugDataVisible && !Materials.empty() && PassCount==1)
			{
				const core::aabbox3d<float> &box = frust.getBoundingBox();
				core::array< core::aabbox3d<f32> > boxes;
				video::SMaterial m;
				m.Lighting = false;
				driver->setMaterial(m);
				if ( DebugDataVisible & scene::EDS_BBOX_BUFFERS )
				{
					StdOctTree->renderBoundingBoxes(box, boxes);
					for (u32 b=0; b<boxes.size(); ++b)
						driver->draw3DBox(boxes[b], video::SColor(0,255,255,255));
				}

				if ( DebugDataVisible & scene::EDS_BBOX )
					driver->draw3DBox(Box,video::SColor(0,255,0,0));
			}
			break;

		}
	case video::EVT_2TCOORDS:
		{
			//LightMapOctTree->calculatePolys(box);
			LightMapOctTree->calculatePolys(frust);

			OctTree<video::S3DVertex2TCoords>::SIndexData* d =  LightMapOctTree->getIndexData();

			for (u32 i=0; i<Materials.size(); ++i)
			{
				if ( 0 == d[i].CurrentSize )
					continue;

				video::IMaterialRenderer* rnd = driver->getMaterialRenderer(Materials[i].MaterialType);
				bool transparent = (rnd && rnd->isTransparent());

				// only render transparent buffer if this is the transparent render pass
				// and solid only in solid pass
				if (transparent == isTransparentPass)
				{
					driver->setMaterial(Materials[i]);
					driver->drawIndexedTriangleList(
						&LightMapMeshes[i].Vertices[0], LightMapMeshes[i].Vertices.size(),
						d[i].Indices, d[i].CurrentSize / 3);
				}
			}

			// for debug purposes only
			if (DebugDataVisible && !Materials.empty() && PassCount==1)
			{
				const core::aabbox3d<float> &box = frust.getBoundingBox();
				core::array< core::aabbox3d<f32> > boxes;
				video::SMaterial m;
				m.Lighting = false;
				driver->setMaterial(m);
				if ( DebugDataVisible & scene::EDS_BBOX_BUFFERS )
				{
					LightMapOctTree->renderBoundingBoxes(box, boxes);
					for (u32 b=0; b<boxes.size(); ++b)
						driver->draw3DBox(boxes[b], video::SColor(0,255,255,255));
				}

				if ( DebugDataVisible & scene::EDS_BBOX )
					driver->draw3DBox(Box,video::SColor(0,255,0,0));
			}
		}
		break;
	}
}
	void CSceneNodeAnimatorCameraFPS::animateNode(ISceneNode* node, UINT32 timeMs)
	{
		if (!node || node->getType() != ESNT_CAMERA)
			return;

		ICameraSceneNode* camera = static_cast<ICameraSceneNode*>(node);

		if (firstUpdate)
		{
			camera->updateAbsolutePosition();
			if (CursorControl)
			{
				CursorControl->setPosition(0.5f, 0.5f);
				CursorPos = CenterCursor = CursorControl->getRelativePosition();
			}

			LastAnimationTime = timeMs;

			firstUpdate = false;
		}

		// If the camera isn't the active camera, and receiving input, then don't process it.
		if (!camera->isInputReceiverEnabled())
		{
			firstInput = true;
			return;
		}

		if (firstInput)
		{
			allKeysUp();
			firstInput = false;
		}

		ISceneManager * smgr = camera->getSceneManager();
		if (smgr && smgr->getActiveCamera() != camera)
			return;

		// get time
		FLOAT32 timeDiff = (FLOAT32)(timeMs - LastAnimationTime);
		LastAnimationTime = timeMs;

		// update position
		Vector3 pos = camera->getPosition();

		// Update rotation
		Vector3 target = (camera->getTarget() - camera->getAbsolutePosition());
		Vector3 relativeRotation = target.getHorizontalAngle();

		if (CursorControl)
		{
			if (CursorPos != CenterCursor)
			{
				relativeRotation.y -= (0.5f - CursorPos.x) * RotateSpeed;
				relativeRotation.x -= (0.5f - CursorPos.y) * RotateSpeed * MouseYDirection;

				// X < MaxVerticalAngle or X > 360-MaxVerticalAngle

				if (relativeRotation.x > MaxVerticalAngle * 2 &&
					relativeRotation.x < 360.0f - MaxVerticalAngle)
				{
					relativeRotation.x = 360.0f - MaxVerticalAngle;
				}
				else
					if (relativeRotation.x > MaxVerticalAngle &&
						relativeRotation.x < 360.0f - MaxVerticalAngle)
					{
						relativeRotation.x = MaxVerticalAngle;
					}

				// Do the fix as normal, special case below
				// reset cursor position to the centre of the window.
				CursorControl->setPosition(0.5f, 0.5f);
				CenterCursor = CursorControl->getRelativePosition();

				// needed to avoid problems when the event receiver is disabled
				CursorPos = CenterCursor;
			}

			// Special case, mouse is whipped outside of window before it can update.
			IVideoDriver* driver = smgr->getVideoDriver();
			Vector2 mousepos(UINT32(CursorControl->getPosition().x), UINT32(CursorControl->getPosition().y));
			rect<UINT32> screenRect(0, 0, driver->getScreenSize().Width, driver->getScreenSize().Height);

			// Only if we are moving outside quickly.
			bool reset = !screenRect.isPointInside(mousepos);

			if (reset)
			{
				// Force a reset.
				CursorControl->setPosition(0.5f, 0.5f);
				CenterCursor = CursorControl->getRelativePosition();
				CursorPos = CenterCursor;
			}
		}

		// set target

		target.set(0, 0, Math::_max<Real>(1.f, pos.length()));
		Vector3 movedir = target;

		Matrix4 mat;
		mat.setRotationDegrees(Vector3(relativeRotation.x, relativeRotation.y, 0));
		//mat.transformVect(target);
		target = mat.transformAffine(target);

		if (NoVerticalMovement)
		{
			mat.setRotationDegrees(Vector3(0, relativeRotation.y, 0));
			//mat.transformVect(movedir);
			target = mat.transformAffine(target);
		}
		else
		{
			movedir = target;
		}

		movedir.normalise();

		if (CursorKeys[EKA_MOVE_FORWARD])
			pos += movedir * timeDiff * MoveSpeed;

		if (CursorKeys[EKA_MOVE_BACKWARD])
			pos -= movedir * timeDiff * MoveSpeed;

		// strafing

		Vector3 strafevect = target;
		strafevect = strafevect.crossProduct(camera->getUpVector());

		if (NoVerticalMovement)
			strafevect.y = 0.0f;

		strafevect.normalise();

		if (CursorKeys[EKA_STRAFE_LEFT])
			pos += strafevect * timeDiff * MoveSpeed;

		if (CursorKeys[EKA_STRAFE_RIGHT])
			pos -= strafevect * timeDiff * MoveSpeed;

		// For jumping, we find the collision response animator attached to our camera
		// and if it's not falling, we tell it to jump.
		if (CursorKeys[EKA_JUMP_UP])
		{
			const ISceneNodeAnimatorList& animators = camera->getAnimators();
			ISceneNodeAnimatorList::const_iterator it = animators.begin();
			while (it != animators.end())
			{
				if (ESNAT_COLLISION_RESPONSE == (*it)->getType())
				{
					ISceneNodeAnimatorCollisionResponse * collisionResponse =
						static_cast<ISceneNodeAnimatorCollisionResponse *>(*it);

					if (!collisionResponse->isFalling())
						collisionResponse->jump(JumpSpeed);
				}

				it++;
			}
		}

		// write translation
		camera->setPosition(pos);

		// write right target
		target += pos;
		camera->setTarget(target);
	}
//! render
void CParticleSystemSceneNode::render()
{
	video::IVideoDriver* driver = SceneManager->getVideoDriver();
	ICameraSceneNode* camera = SceneManager->getActiveCamera();

	if (!camera || !driver)
		return;


#if 0
	// calculate vectors for letting particles look to camera
	core::vector3df view(camera->getTarget() - camera->getAbsolutePosition());
	view.normalize();

	core::vector3df horizontal = camera->getUpVector().crossProduct(view);
	horizontal.normalize();
	horizontal *= 0.5f * ParticleSize.Width;

	core::vector3df vertical = horizontal.crossProduct(view);
	vertical.normalize();
	vertical *= 0.5f * ParticleSize.Height;

	view *= -1.0f;

#else

	const core::matrix4 &m = camera->getViewFrustum()->Matrices [ video::ETS_VIEW ];

	f32 f;

	f = 0.5f * ParticleSize.Width;
	const core::vector3df horizontal ( m[0] * f, m[4] * f, m[8] * f );

	f = -0.5f * ParticleSize.Height;
	const core::vector3df vertical ( m[1] * f, m[5] * f, m[9] * f );

	const core::vector3df view ( -m[2], -m[6] , -m[10] );

#endif

	// reallocate arrays, if they are too small
	reallocateBuffers();

	// create particle vertex data
	s32 idx = 0;
	for (u32 i=0; i<Particles.size(); ++i)
	{
		const SParticle& particle = Particles[i];

		Buffer.Vertices[0+idx].Pos = particle.pos + horizontal + vertical;
		Buffer.Vertices[0+idx].Color = particle.color;
		Buffer.Vertices[0+idx].Normal = view;

		Buffer.Vertices[1+idx].Pos = particle.pos + horizontal - vertical;
		Buffer.Vertices[1+idx].Color = particle.color;
		Buffer.Vertices[1+idx].Normal = view;

		Buffer.Vertices[2+idx].Pos = particle.pos - horizontal - vertical;
		Buffer.Vertices[2+idx].Color = particle.color;
		Buffer.Vertices[2+idx].Normal = view;

		Buffer.Vertices[3+idx].Pos = particle.pos - horizontal + vertical;
		Buffer.Vertices[3+idx].Color = particle.color;
		Buffer.Vertices[3+idx].Normal = view;

		idx +=4;
	}

	// render all
	core::matrix4 mat;
	if (!ParticlesAreGlobal)
		mat.setTranslation(AbsoluteTransformation.getTranslation());
	driver->setTransform(video::ETS_WORLD, mat);

	driver->setMaterial(Buffer.Material);

	driver->drawVertexPrimitiveList(Buffer.getVertices(), Particles.size()*4,
		Buffer.getIndices(), Particles.size()*2, video::EVT_STANDARD, EPT_TRIANGLES);

	// for debug purposes only:
	if ( DebugDataVisible & scene::EDS_BBOX )
	{
		driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
		video::SMaterial deb_m;
		deb_m.Lighting = false;
		driver->setMaterial(deb_m);
		driver->draw3DBox(Buffer.BoundingBox, video::SColor(0,255,255,255));
	}
}
//! render
void CBillboardSceneNode::render()
{
	video::IVideoDriver* driver = SceneManager->getVideoDriver();
	ICameraSceneNode* camera = SceneManager->getActiveCamera();

	if (!camera || !driver)
		return;

	// make billboard look to camera

	core::vector3df pos = getAbsolutePosition();

	core::vector3df campos = camera->getAbsolutePosition();
	core::vector3df target = camera->getTarget();
	core::vector3df up = camera->getUpVector();
	core::vector3df view = target - campos;
	view.normalize();

	core::vector3df horizontal = up.crossProduct(view);
	if ( horizontal.getLength() == 0 )
	{
		horizontal.set(up.Y,up.X,up.Z);
	}
	horizontal.normalize();
	core::vector3df topHorizontal = horizontal * 0.5f * TopEdgeWidth;
	horizontal *= 0.5f * Size.Width;

	// pointing down!
	core::vector3df vertical = horizontal.crossProduct(view);
	vertical.normalize();
	vertical *= 0.5f * Size.Height;

	view *= -1.0f;

	for (s32 i=0; i<4; ++i)
		vertices[i].Normal = view;

	/* Vertices are:
	2--1
	|\ |
	| \|
	3--0
	*/
	vertices[0].Pos = pos + horizontal + vertical;
	vertices[1].Pos = pos + topHorizontal - vertical;
	vertices[2].Pos = pos - topHorizontal - vertical;
	vertices[3].Pos = pos - horizontal + vertical;

	// draw

	if (DebugDataVisible & scene::EDS_BBOX)
	{
		driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
		video::SMaterial m;
		m.Lighting = false;
		driver->setMaterial(m);
		driver->draw3DBox(BBox, video::SColor(0,208,195,152));
	}

	driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);

	driver->setMaterial(Material);

	driver->drawIndexedTriangleList(vertices, 4, indices, 2);
}
//! OnAnimate() is called just before rendering the whole scene.
void CSceneNodeAnimatorCameraMaya::animateNode(ISceneNode *node, u32 timeMs)
{
	//Alt + LM = Rotate around camera pivot
	//Alt + LM + MM = Dolly forth/back in view direction (speed % distance camera pivot - max distance to pivot)
	//Alt + MM = Move on camera plane (Screen center is about the mouse pointer, depending on move speed)

	if (!node || node->getType() != ESNT_CAMERA)
		return;

	ICameraSceneNode* camera = static_cast<ICameraSceneNode*>(node);

	// If the camera isn't the active camera, and receiving input, then don't process it.
	if (!camera->isInputReceiverEnabled())
		return;

	scene::ISceneManager * smgr = camera->getSceneManager();
	if (smgr && smgr->getActiveCamera() != camera)
		return;

	if (OldCamera != camera)
	{
		LastCameraTarget = OldTarget = camera->getTarget();
		OldCamera = camera;
	}
	else
	{
		OldTarget += camera->getTarget() - LastCameraTarget;
	}

	f32 nRotX = RotX;
	f32 nRotY = RotY;
	f32 nZoom = CurrentZoom;

	if ( (isMouseKeyDown(0) && isMouseKeyDown(2)) || isMouseKeyDown(1) )
	{
		if (!Zooming)
		{
			ZoomStart = MousePos;
			Zooming = true;
		}
		else
		{
			const f32 targetMinDistance = 0.1f;
			nZoom += (ZoomStart.X - MousePos.X) * ZoomSpeed;

			if (nZoom < targetMinDistance) // jox: fixed bug: bounce back when zooming to close
				nZoom = targetMinDistance;
		}
	}
	else if (Zooming)
	{
		const f32 old = CurrentZoom;
		CurrentZoom = CurrentZoom + (ZoomStart.X - MousePos.X ) * ZoomSpeed;
		nZoom = CurrentZoom;

		if (nZoom < 0)
			nZoom = CurrentZoom = old;
		Zooming = false;
	}

	// Translation ---------------------------------

	core::vector3df translate(OldTarget);
	const core::vector3df upVector(camera->getUpVector());
	const core::vector3df target = camera->getTarget();

	core::vector3df pos = camera->getPosition();
	core::vector3df tvectX = pos - target;
	tvectX = tvectX.crossProduct(upVector);
	tvectX.normalize();

	const SViewFrustum* const va = camera->getViewFrustum();
	core::vector3df tvectY = (va->getFarLeftDown() - va->getFarRightDown());
	tvectY = tvectY.crossProduct(upVector.Y > 0 ? pos - target : target - pos);
	tvectY.normalize();

	if (isMouseKeyDown(2) && !Zooming)
	{
		if (!Translating)
		{
			TranslateStart = MousePos;
			Translating = true;
		}
		else
		{
			translate +=  tvectX * (TranslateStart.X - MousePos.X)*TranslateSpeed +
			              tvectY * (TranslateStart.Y - MousePos.Y)*TranslateSpeed;
		}
	}
	else if (Translating)
	{
		translate += tvectX * (TranslateStart.X - MousePos.X)*TranslateSpeed +
		             tvectY * (TranslateStart.Y - MousePos.Y)*TranslateSpeed;
		OldTarget = translate;
		Translating = false;
	}

	// Rotation ------------------------------------

	if (isMouseKeyDown(0) && !Zooming)
	{
		if (!Rotating)
		{
			RotateStart = MousePos;
			Rotating = true;
			nRotX = RotX;
			nRotY = RotY;
		}
		else
		{
			nRotX += (RotateStart.X - MousePos.X) * RotateSpeed;
			nRotY += (RotateStart.Y - MousePos.Y) * RotateSpeed;
		}
	}
	else if (Rotating)
	{
		RotX += (RotateStart.X - MousePos.X) * RotateSpeed;
		RotY += (RotateStart.Y - MousePos.Y) * RotateSpeed;
		nRotX = RotX;
		nRotY = RotY;
		Rotating = false;
	}

	// Set pos ------------------------------------

	pos = translate;
	pos.X += nZoom;

	pos.rotateXYBy(nRotY, translate);
	pos.rotateXZBy(-nRotX, translate);

	camera->setPosition(pos);
	camera->setTarget(translate);

	// Rotation Error ----------------------------

	// jox: fixed bug: jitter when rotating to the top and bottom of y
	pos.set(0,1,0);
	pos.rotateXYBy(-nRotY);
	pos.rotateXZBy(-nRotX+180.f);
	camera->setUpVector(pos);
	LastCameraTarget = camera->getTarget();
}
void CSceneNodeAnimatorCameraFPS::animateNode(ISceneNode* node, u32 timeMs)
{
	if (node->getType() != ESNT_CAMERA)
		return;

	ICameraSceneNode* camera = static_cast<ICameraSceneNode*>(node);

	if (firstUpdate)
	{
		camera->updateAbsolutePosition();
		if (CursorControl && camera)
		{
			CursorControl->setPosition(0.5f, 0.5f);
			CursorPos = CenterCursor = CursorControl->getRelativePosition();
		}

		LastAnimationTime = timeMs;

		firstUpdate = false;
	}

	// get time
	f32 timeDiff = (f32) ( timeMs - LastAnimationTime );
	LastAnimationTime = timeMs;

	// update position
	core::vector3df pos = camera->getPosition();

	// Update rotation
	core::vector3df target = (camera->getTarget() - camera->getAbsolutePosition());
	core::vector3df relativeRotation = target.getHorizontalAngle();

	if (CursorControl)
	{
		if (CursorPos != CenterCursor)
		{
			relativeRotation.Y -= (0.5f - CursorPos.X) * RotateSpeed;
			relativeRotation.X -= (0.5f - CursorPos.Y) * RotateSpeed;

			// X < MaxVerticalAngle or X > 360-MaxVerticalAngle

			if (relativeRotation.X > MaxVerticalAngle*2 &&
				relativeRotation.X < 360.0f-MaxVerticalAngle)
			{
				relativeRotation.X = 360.0f-MaxVerticalAngle;
			}
			else
			if (relativeRotation.X > MaxVerticalAngle &&
				relativeRotation.X < 360.0f-MaxVerticalAngle)
			{
				relativeRotation.X = MaxVerticalAngle;
			}

			// reset cursor position
			CursorControl->setPosition(0.5f, 0.5f);
			CenterCursor = CursorControl->getRelativePosition();
			// needed to avoid problems when the ecent receiver is
			// disabled
			CursorPos = CenterCursor;
		}
	}

	// set target

	target.set(0,0, core::max_(1.f, pos.getLength()));
	core::vector3df movedir = target;

	core::matrix4 mat;
	mat.setRotationDegrees(core::vector3df(relativeRotation.X, relativeRotation.Y, 0));
	mat.transformVect(target);

	if (NoVerticalMovement)
	{
		mat.setRotationDegrees(core::vector3df(0, relativeRotation.Y, 0));
		mat.transformVect(movedir);
	}
	else
	{
		movedir = target;
	}

	movedir.normalize();

	if (CursorKeys[EKA_MOVE_FORWARD])
		pos += movedir * timeDiff * MoveSpeed;

	if (CursorKeys[EKA_MOVE_BACKWARD])
		pos -= movedir * timeDiff * MoveSpeed;

	// strafing

	core::vector3df strafevect = target;
	strafevect = strafevect.crossProduct(camera->getUpVector());

	if (NoVerticalMovement)
		strafevect.Y = 0.0f;

	strafevect.normalize();

	if (CursorKeys[EKA_STRAFE_LEFT])
		pos += strafevect * timeDiff * MoveSpeed;

	if (CursorKeys[EKA_STRAFE_RIGHT])
		pos -= strafevect * timeDiff * MoveSpeed;

	// For jumping, we find the collision response animator attached to our camera
	// and if it's not falling, we tell it to jump.
	if (CursorKeys[EKA_JUMP_UP])
	{
		const core::list<ISceneNodeAnimator*> & animators = camera->getAnimators();
		core::list<ISceneNodeAnimator*>::ConstIterator it = animators.begin();
		while(it != animators.end())
		{
			if(ESNAT_COLLISION_RESPONSE == (*it)->getType())
			{
				ISceneNodeAnimatorCollisionResponse * collisionResponse = 
					static_cast<ISceneNodeAnimatorCollisionResponse *>(*it);

				if(!collisionResponse->isFalling())
					collisionResponse->jump(JumpSpeed);
			}

			it++;
		}
	}

	// write translation
	camera->setPosition(pos);

	// write right target

	TargetVector = target;
	target += pos;
	camera->setTarget(target);
}
void CBillboardGroupSceneNode::updateBillboards()
{
    ICameraSceneNode* camera = SceneManager->getActiveCamera();
    
    if ( !camera )
        return;
    
    core::vector3df camPos = camera->getAbsolutePosition();
        
    core::vector3df ref = core::vector3df(0,1,0);
    
    camera->getAbsoluteTransformation().rotateVect(ref);
    
    core::vector3df view, right, up;
    
    bool farAway = false;
    
    core::vector3df center = BoundingBox.getCenter();
    AbsoluteTransformation.transformVect(center);
    
    core::vector3df camDir = camPos - center;
    
    if ( camDir.getLengthSQ() >= (FarDistance + Radius)*(FarDistance + Radius) )
    {
        farAway = true;
        view = center - camPos;
        view.normalize();
        
        right = ref.crossProduct( view );
        
        up = view.crossProduct( right );
    }
    
    core::vector3df rotatedCamDir = camDir;
    AbsoluteTransformation.inverseRotateVect( rotatedCamDir );
    
    if ( farAway && (rotatedCamDir - LastCamDir).getLengthSQ() < 1000.0f )
    {
        return;
    }
    
    LastCamDir = rotatedCamDir;
    
    // Update the position of every billboard
    for ( s32 i=0; i<Billboards.size(); i++ )
    {
        if ( !farAway )
        {
            core::vector3df pos = Billboards[i].Position;
            
            AbsoluteTransformation.transformVect( pos );
            
            view = pos - camPos;
            
            view.normalize();
        }
        
        core::vector3df thisRight = right;
        core::vector3df thisUp = up;
        
        if ( Billboards[i].HasAxis )
        {
            core::vector3df axis = Billboards[i].Axis;
            
            AbsoluteTransformation.rotateVect(axis);
            
            thisRight = axis.crossProduct( view );
            
            thisUp = axis;
        }
        else if ( !farAway )
        {
            thisRight = ref.crossProduct( view );
            
            thisUp = view.crossProduct( thisRight );
        }
        
        f32 rollrad = Billboards[i].Roll * core::DEGTORAD;
        f32 cos_roll = cos( rollrad );
        f32 sin_roll = sin( rollrad );
        
        core::vector3df a =  cos_roll * thisRight + sin_roll * thisUp;
        core::vector3df b = -sin_roll * thisRight + cos_roll * thisUp;
        
        a *= Billboards[i].Size.Width / 2.0f;
        b *= Billboards[i].Size.Height / 2.0f;
        
        s32 vertexIndex = 4 * i;  // 4 vertices per billboard
        
        core::vector3df billPos = Billboards[i].Position;
        
        AbsoluteTransformation.transformVect(billPos);
        
        MeshBuffer.Vertices[vertexIndex  ].Pos = billPos - a + b;
        MeshBuffer.Vertices[vertexIndex+1].Pos = billPos + a + b;
        MeshBuffer.Vertices[vertexIndex+2].Pos = billPos + a - b;
        MeshBuffer.Vertices[vertexIndex+3].Pos = billPos - a - b;
    }
}