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

    • Ansome

      Turns out my 15th anniversary mission idea has already been done once or twice before! I've been beaten to the punch once again, but I suppose that's to be expected when there's over 170 FMs out there, eh? I'm not complaining though, I love learning new tricks and taking inspiration from past FMs. Best of luck on your own fan missions!
      · 4 replies
    • The Black Arrow

      I wanna play Doom 3, but fhDoom has much better features than dhewm3, yet fhDoom is old, outdated and probably not supported. Damn!
      Makes me think that TDM engine for Doom 3 itself would actually be perfect.
      · 6 replies
    • Petike the Taffer

      Maybe a bit of advice ? In the FM series I'm preparing, the two main characters have the given names Toby and Agnes (it's the protagonist and deuteragonist, respectively), I've been toying with the idea of giving them family names as well, since many of the FM series have named protagonists who have surnames. Toby's from a family who were usually farriers, though he eventually wound up working as a cobbler (this serves as a daylight "front" for his night time thieving). Would it make sense if the man's popularly accepted family name was Farrier ? It's an existing, though less common English surname, and it directly refers to the profession practiced by his relatives. Your suggestions ?
      · 9 replies
    • nbohr1more

      Looks like the "Reverse April Fools" releases were too well hidden. Darkfate still hasn't acknowledge all the new releases. Did you play any of the new April Fools missions?
      · 5 replies
    • The Black Arrow

      Hope everyone has the blessing of undying motivation for "The Dark Mod 15th Anniversary Contest". Can't wait to see the many magnificent missions you all may have planned. Good luck, with an Ace!
      · 0 replies
×
×
  • Create New...