Jump to content

Clean spotlight rotation.


mkultra333
 Share

Recommended Posts

I noticed that if you do 90 degree rotations of spotlights via the menu you get a little floating point noise in the light's specifications.

 

I made some code that makes the 90 degree rotation of spotlights clean. Doesn't seem to cause problems, though I don't know DarkRadiant well enough to really be sure. I modified void Light::rotate(const Quaternion& rotation) so that it detects 90 degree rotations and then uses flips and swaps to achieve that rotation rather than trigonometry.

 

Here's the code, I don't know if you'd want this or not but feel free to use it if you do, GPL license.

 

   #define BZN_ROTEPSILON	0.000001
  void Light::rotate(const Quaternion& rotation) {
   if (isProjected()) {

	   Quaternion NewRotation = rotation;

	   if(			// X Axis 90 degree rotation
					   (rotation.w()>-0.707107-BZN_ROTEPSILON) && (rotation.w()<-0.707107+BZN_ROTEPSILON) 
			   &&  (rotation.x()>-0.707107-BZN_ROTEPSILON) && (rotation.x()<-0.707107+BZN_ROTEPSILON)
			   &&  (rotation.y()>0.0-BZN_ROTEPSILON) && (rotation.y()<0.0+BZN_ROTEPSILON)
			   &&  (rotation.z()>0.0-BZN_ROTEPSILON) && (rotation.z()<0.0+BZN_ROTEPSILON)
		   )
	   {
		   Vector3 LTarget;

		   LTarget=Vector3(_lightTarget.x(), -_lightTarget.z(), _lightTarget.y());
		   _lightTarget=LTarget;

		   LTarget=Vector3(_lightRight.x(), -_lightRight.z(), _lightRight.y());
		   _lightRight=LTarget;

		   LTarget=Vector3(_lightUp.x(), -_lightUp.z(), _lightUp.y());
		   _lightUp=LTarget;

		   LTarget=Vector3(_lightStart.x(), -_lightStart.z(), _lightStart.y());
		   _lightStart=LTarget;

		   LTarget=Vector3(_lightEnd.x(), -_lightEnd.z(), _lightEnd.y());
		   _lightEnd=LTarget;

		   NewRotation=Quaternion(1.0, 0, 0, 0);	
	   }

	   if(			// Y Axis 90 degree rotation
					   (rotation.w()>0.707107-BZN_ROTEPSILON) && (rotation.w()<0.707107+BZN_ROTEPSILON) 
			   &&  (rotation.x()>0.0-BZN_ROTEPSILON) && (rotation.x()<0.0+BZN_ROTEPSILON)
			   &&  (rotation.y()>0.707107-BZN_ROTEPSILON) && (rotation.y()<0.707107+BZN_ROTEPSILON)
			   &&  (rotation.z()>0.0-BZN_ROTEPSILON) && (rotation.z()<0.0+BZN_ROTEPSILON)
		   )
	   {
		   Vector3 LTarget;

		   LTarget=Vector3(_lightTarget.z(), -_lightTarget.y(), _lightTarget.x());
		   _lightTarget=LTarget;

		   LTarget=Vector3(_lightRight.z(), -_lightRight.y(), _lightRight.x());
		   _lightRight=LTarget;

		   LTarget=Vector3(_lightUp.z(), -_lightUp.y(), _lightUp.x());
		   _lightUp=LTarget;

		   LTarget=Vector3(_lightStart.z(), -_lightStart.y(), _lightStart.x());
		   _lightStart=LTarget;

		   LTarget=Vector3(_lightEnd.z(), -_lightEnd.y(), _lightEnd.x());
		   _lightEnd=LTarget;

		   NewRotation=Quaternion(1.0, 0, 0, 0);	
	   }

	   if(			// Z Axis 90 degree rotation
					   (rotation.w()>-0.707107-BZN_ROTEPSILON) && (rotation.w()<-0.707107+BZN_ROTEPSILON) 
			   &&  (rotation.x()>0.0-BZN_ROTEPSILON) && (rotation.x()<0.0+BZN_ROTEPSILON)
			   &&  (rotation.y()>0.0-BZN_ROTEPSILON) && (rotation.y()<0.0+BZN_ROTEPSILON)
			   &&  (rotation.z()>-0.707107-BZN_ROTEPSILON) && (rotation.z()<-0.707107+BZN_ROTEPSILON)
		   )
	   {
		   Vector3 LTarget;

		   LTarget=Vector3(-_lightTarget.y(), -_lightTarget.x(), -_lightTarget.z());
		   _lightTarget=LTarget;

		   LTarget=Vector3(-_lightRight.y(), -_lightRight.x(), -_lightRight.z());
		   _lightRight=LTarget;

		   LTarget=Vector3(-_lightUp.y(), -_lightUp.x(), -_lightUp.z());
		   _lightUp=LTarget;

		   LTarget=Vector3(-_lightStart.y(), -_lightStart.x(), -_lightStart.z());
		   _lightStart=LTarget;

		   LTarget=Vector3(-_lightEnd.y(), -_lightEnd.x(), -_lightEnd.z());
		   _lightEnd=LTarget;

		   NewRotation=Quaternion(1.0, 0, 0, 0);	
	   }





	   // Retrieve the rotation matrix...
	   Matrix4 rotationMatrix = matrix4_rotation_for_quaternion(NewRotation);



	   // ... and apply it to all the vertices defining the projection
	   _lightTargetTransformed = rotationMatrix.transform(_lightTarget).getProjected();
	   _lightRightTransformed = rotationMatrix.transform(_lightRight).getProjected();
	   _lightUpTransformed = rotationMatrix.transform(_lightUp).getProjected();
	   _lightStartTransformed = rotationMatrix.transform(_lightStart).getProjected();
	   _lightEndTransformed = rotationMatrix.transform(_lightEnd).getProjected();







   }
   else {
	   m_rotation.rotate(rotation);
   }
  }

Link to comment
Share on other sites

I just tried flipping a point light, and it doesn't respond to these operations at all (as intended). I couldn't notice any glitches with 90 degree rotations neither (I'm using the buttons on the left-hand toolbar). The light just rotates and the rotation matrix always consists of integers, no floating point artifacts.

 

Any steps to reproduce that problem?

Link to comment
Share on other sites

Here's the setup spotlight, it's at an angle to make later problems more noticable, but it'll happen for axial directions too.

 

darkradiant0.jpg

 

 

Now I rotate it 90 degrees around the x (I used the button this time, but the menu option does the same). The light draws ok, but notice the lighting keys, they have things like "4.38125e-006" in them, scientific notation. While this works ok, it's still slightly innaccurate. Probably not a big issue, especially for TDM mappers, but it needed to be changed for my project and the method I suggested gets rid of it.

 

darkradiant1.jpg

 

The issue with mirroring is more of a problem. Here's the same original light, only now it's part of a small scene.

darkradiant2.jpg

 

I flip the scene about the x axis via the button (menu does the same) and this is the result.

 

darkradiant3.jpg

 

With just the light selected, we see the keys. Part of the problem is that light_target really needs it's x value to be flipped, it should be -256.

 

darkradiant4.jpg

 

I have got flipping working now, via my own crude methods. I added the following function.

 

// bzn mirroring for projected lights
// check if the scale is a type of mirroring, if so flip the necessary values
void Light::mirror(const Vector3& scale)
{
if (isProjected()) 
{
	if((scale.x()==-1.0) && (scale.y()==1.0) && (scale.z()==1.0))
	{
		Vector3 LTarget;

		LTarget=Vector3(-_lightTarget.x(), _lightTarget.y(), _lightTarget.z());
		_lightTarget=LTarget;

		LTarget=Vector3(-_lightRight.x(), _lightRight.y(), _lightRight.z());
		_lightRight=LTarget;

		LTarget=Vector3(-_lightUp.x(), _lightUp.y(), _lightUp.z());
		_lightUp=LTarget;

		LTarget=Vector3(-_lightStart.x(), _lightStart.y(), _lightStart.z());
		_lightStart=LTarget;

		LTarget=Vector3(-_lightEnd.x(), _lightEnd.y(), _lightEnd.z());
		_lightEnd=LTarget;	
	}
	else
	if((scale.x()==1.0) && (scale.y()==-1.0) && (scale.z()==1.0))
	{
		Vector3 LTarget;

		LTarget=Vector3(_lightTarget.x(), -_lightTarget.y(), _lightTarget.z());
		_lightTarget=LTarget;

		LTarget=Vector3(_lightRight.x(), -_lightRight.y(), _lightRight.z());
		_lightRight=LTarget;

		LTarget=Vector3(_lightUp.x(), -_lightUp.y(), _lightUp.z());
		_lightUp=LTarget;

		LTarget=Vector3(_lightStart.x(), -_lightStart.y(), _lightStart.z());
		_lightStart=LTarget;

		LTarget=Vector3(_lightEnd.x(), -_lightEnd.y(), _lightEnd.z());
		_lightEnd=LTarget;	
	}
	else
	if((scale.x()==1.0) && (scale.y()==1.0) && (scale.z()==-1.0))
	{
		Vector3 LTarget;

		LTarget=Vector3(_lightTarget.x(), _lightTarget.y(), -_lightTarget.z());
		_lightTarget=LTarget;

		LTarget=Vector3(_lightRight.x(), _lightRight.y(), -_lightRight.z());
		_lightRight=LTarget;

		LTarget=Vector3(_lightUp.x(), _lightUp.y(), -_lightUp.z());
		_lightUp=LTarget;

		LTarget=Vector3(_lightStart.x(), _lightStart.y(), -_lightStart.z());
		_lightStart=LTarget;

		LTarget=Vector3(_lightEnd.x(), _lightEnd.y(), -_lightEnd.z());
		_lightEnd=LTarget;	
	}

}


}

 

... and added a call to it here

 

void LightNode::evaluateTransform() {
if (getType() == TRANSFORM_PRIMITIVE) {
	_light.mirror(getScale());
	_light.translate(getTranslation());
	_light.rotate(getRotation());
}
else {...

 

Not that this is the best way, I just use spotlights, flipping and 90 degree rotation a lot, so it was handy for me.

 

I don't know if this is only happening for me.

Edited by mkultra333
Link to comment
Share on other sites

Ah, you were using projected lights. I was thinking you are working on point lights...

 

I'll check out your fixes - a general scale() routine can be handy for lights as well, I reckon. Your method could probably be expanded so that it can handle general scales too.

Link to comment
Share on other sites

Ah, today I realized the swaps/flips I gave are wrong, they work the first 90 degree rotation but then "undo" themselves on the second.

 

Heres the correct code:

 

   #define BZN_ROTEPSILON	0.000001
  void Light::rotate(const Quaternion& rotation) {
   if (isProjected()) {

	   Quaternion NewRotation = rotation;

	   if(			// X Axis 90 degree rotation
					   (rotation.w()>-0.707107-BZN_ROTEPSILON) && (rotation.w()<-0.707107+BZN_ROTEPSILON)
			   &&  (rotation.x()>-0.707107-BZN_ROTEPSILON) && (rotation.x()<-0.707107+BZN_ROTEPSILON)
			   &&  (rotation.y()>0.0-BZN_ROTEPSILON) && (rotation.y()<0.0+BZN_ROTEPSILON)
			   &&  (rotation.z()>0.0-BZN_ROTEPSILON) && (rotation.z()<0.0+BZN_ROTEPSILON)
		   )
	   {
		   Vector3 LTarget;

		   LTarget=Vector3(_lightTarget.x(), _lightTarget.z(), -_lightTarget.y());
		   _lightTarget=LTarget;

		   LTarget=Vector3(_lightRight.x(), _lightRight.z(), -_lightRight.y());
		   _lightRight=LTarget;

		   LTarget=Vector3(_lightUp.x(), _lightUp.z(), -_lightUp.y());
		   _lightUp=LTarget;

		   LTarget=Vector3(_lightStart.x(), _lightStart.z(), -_lightStart.y());
		   _lightStart=LTarget;

		   LTarget=Vector3(_lightEnd.x(), _lightEnd.z(), -_lightEnd.y());
		   _lightEnd=LTarget;

		   NewRotation=Quaternion(1.0, 0, 0, 0);	
	   }

	   if(			// Y Axis 90 degree rotation
					   (rotation.w()>0.707107-BZN_ROTEPSILON) && (rotation.w()<0.707107+BZN_ROTEPSILON)
			   &&  (rotation.x()>0.0-BZN_ROTEPSILON) && (rotation.x()<0.0+BZN_ROTEPSILON)
			   &&  (rotation.y()>0.707107-BZN_ROTEPSILON) && (rotation.y()<0.707107+BZN_ROTEPSILON)
			   &&  (rotation.z()>0.0-BZN_ROTEPSILON) && (rotation.z()<0.0+BZN_ROTEPSILON)
		   )
	   {
		   Vector3 LTarget;

		   LTarget=Vector3(_lightTarget.z(), _lightTarget.y(), -_lightTarget.x());
		   _lightTarget=LTarget;

		   LTarget=Vector3(_lightRight.z(), _lightRight.y(), -_lightRight.x());
		   _lightRight=LTarget;

		   LTarget=Vector3(_lightUp.z(), _lightUp.y(), -_lightUp.x());
		   _lightUp=LTarget;

		   LTarget=Vector3(_lightStart.z(), _lightStart.y(), -_lightStart.x());
		   _lightStart=LTarget;

		   LTarget=Vector3(_lightEnd.z(), _lightEnd.y(), -_lightEnd.x());
		   _lightEnd=LTarget;

		   NewRotation=Quaternion(1.0, 0, 0, 0);	
	   }

	   if(			// Z Axis 90 degree rotation
					   (rotation.w()>-0.707107-BZN_ROTEPSILON) && (rotation.w()<-0.707107+BZN_ROTEPSILON)
			   &&  (rotation.x()>0.0-BZN_ROTEPSILON) && (rotation.x()<0.0+BZN_ROTEPSILON)
			   &&  (rotation.y()>0.0-BZN_ROTEPSILON) && (rotation.y()<0.0+BZN_ROTEPSILON)
			   &&  (rotation.z()>-0.707107-BZN_ROTEPSILON) && (rotation.z()<-0.707107+BZN_ROTEPSILON)
		   )
	   {
		   Vector3 LTarget;

		   LTarget=Vector3(_lightTarget.y(), -_lightTarget.x(), _lightTarget.z());
		   _lightTarget=LTarget;

		   LTarget=Vector3(_lightRight.y(), -_lightRight.x(), _lightRight.z());
		   _lightRight=LTarget;

		   LTarget=Vector3(_lightUp.y(), -_lightUp.x(), _lightUp.z());
		   _lightUp=LTarget;

		   LTarget=Vector3(_lightStart.y(), -_lightStart.x(), _lightStart.z());
		   _lightStart=LTarget;

		   LTarget=Vector3(_lightEnd.y(), -_lightEnd.x(), _lightEnd.z());
		   _lightEnd=LTarget;

		   NewRotation=Quaternion(1.0, 0, 0, 0);	
	   }





	   // Retrieve the rotation matrix...
	   Matrix4 rotationMatrix = matrix4_rotation_for_quaternion(NewRotation);



	   // ... and apply it to all the vertices defining the projection
	   _lightTargetTransformed = rotationMatrix.transform(_lightTarget).getProjected();
	   _lightRightTransformed = rotationMatrix.transform(_lightRight).getProjected();
	   _lightUpTransformed = rotationMatrix.transform(_lightUp).getProjected();
	   _lightStartTransformed = rotationMatrix.transform(_lightStart).getProjected();
	   _lightEndTransformed = rotationMatrix.transform(_lightEnd).getProjected();







   }
   else {
	   m_rotation.rotate(rotation);
   }
  }

Link to comment
Share on other sites

Ok, thanks. :) I still haven't had a chance to check out the changes, I'm really swamped with stuff right now.

 

Gah! Just as well, I think the above is wrong too... hehe, I'll get back to you.

 

P.S. Don't worry, I'm not pressuring for this to be included soon, or even at all. Just posting it in case you think it's interesting and it saves you some time.

 

Edit: I see what was throwing me. Light::Rotate gets called multiple times for a single light rotation, so the flip/swaps were being applied multiple times. That, plus the difference between _lightTarget and _lightTargetTransformed.

 

Now I change _lightTargetTransformed instead, and only apply the projections at the bottom if the rotation hasn't already been achieved using flip/swaps. If I do that, the second version of the rotation flips/swaps is correct.

Edited by mkultra333
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

  • Recent Status Updates

    • freyk

      Tried to make a tdm advertisement commentpost at one of civvie11 youtube videos about T2. Post got marked as spam. His problem (to not discover TDM for himself),..not my problem.
      But some help of some fellow TDM yt-videocomment posters would be nice. To ask him and others, to play TDM. To get more players/creators. 
      · 0 replies
    • datiswous

      Currently Profile Information has 3 fields, these are shown in forum posts under your avatar:
      1. Gender
      2. Location
      3. Interests
      I think that it could be useful to have an extra field called "Operating system" (under location). It can be useful for tech support and to see what people use.
      Alternatively it could be a more general term, like PC system, so that you can for example state that you use an AMD gpu.
      · 2 replies
    • OrbWeaver

      Greetings fellow kids.
      · 11 replies
    • Crafty_Creeper

      Keep on Creeping on...
       
      · 3 replies
    • datiswous

      Just found out piped.kavin.rocks has a build-in audio-only for video's (great for music video's). Never thought something like this (including browser extensions) excisted. This would have saved me a lot of money on vacation, where I was camping without wifi.
      · 4 replies
×
×
  • Create New...