Jump to content
The Dark Mod Forums

Clean spotlight rotation.


mkultra333

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.

  • Recent Status Updates

    • Petike the Taffer  »  DeTeEff

      I've updated the articles for your FMs and your author category at the wiki. Your newer nickname (DeTeEff) now comes first, and the one in parentheses is your older nickname (Fieldmedic). Just to avoid confusing people who played your FMs years ago and remember your older nickname. I've added a wiki article for your latest FM, Who Watches the Watcher?, as part of my current updating efforts. Unless I overlooked something, you have five different FMs so far.
      · 0 replies
    • Petike the Taffer

      I've finally managed to log in to The Dark Mod Wiki. I'm back in the saddle and before the holidays start in full, I'll be adding a few new FM articles and doing other updates. Written in Stone is already done.
      · 4 replies
    • nbohr1more

      TDM 15th Anniversary Contest is now active! Please declare your participation: https://forums.thedarkmod.com/index.php?/topic/22413-the-dark-mod-15th-anniversary-contest-entry-thread/
       
      · 0 replies
    • JackFarmer

      @TheUnbeholden
      You cannot receive PMs. Could you please be so kind and check your mailbox if it is full (or maybe you switched off the function)?
      · 1 reply
    • OrbWeaver

      I like the new frob highlight but it would nice if it was less "flickery" while moving over objects (especially barred metal doors).
      · 4 replies
×
×
  • Create New...