[Bf-committers] NDOF view transform questions (warning: long post; example code included)

sfogoros sfogoros at att.net
Sun Jun 17 20:50:10 CEST 2007


Greetings Everyone,

In May I bought a SpaceNavigator (SNav, 6dof, ndof), and updated the 
patch "[#3688] 3d mouse support - Spaceball/pilot/traveler" to work on 
blender2.44 release. While everything basically worked, I found the axes 
moved wrong and the implementation was incomplete. First,I needed to 
swap all the axes polarity and reduce the device axes sensitivity (a lot).

In Orthographic, Tz scrolls the screen up/dn, Ty scales the view, and Tx 
did what I expected. Rotation seems to suffer from gimble lock and pivot 
point is always grid 0,0,0 regardless of menu settings (actually, I just 
tested this in 2.43 and pivot point settings seem to be completely broken).

In Perspective, pivot is always on selected object and works for Rz, but 
Rx, Ry are not corrected for current Rz vector and also seems to suffer 
from gimble lock (or just coded wrong). Also, after rotating the view 
with 6dof, G.vd->dist state is altered where mouse rotating no longer 
occurs 'out in front of the viewport' but remains centered on the viewpoint.

I wanted the primary view movement mode of ndof to be a 'flying' type 
movement. An option to this mode is 'game' where y is locked as in 3rd 
or 1st person type view. viewmoveNDOF should also support configuration 
of console controller (xbox, psx type) joystick devices.

I replaced view.c:viewmoveNDOF() with the following code:


void getndof(float *sbval);

void viewmoveNDOF(int mode)
{
	int i, opersp;
	float phi, odist;
	float dval[7];
	// static fval[6] is for low pass filter.
	// device input vector is now dval[6]
	static float fval[6];
	float tvec[3],rvec[3];
	float q1[4];
	float mat[3][3];
	float upvec[3];


	/*----------------------------------------------------
	* sometimes this routine is called from headerbuttons
	* viewmove needs to refresh the screen
	*/
	areawinset(curarea->win);


	/* fetch the current state of the ndof device */

	getndof(dval);


	/* Scale input values
	*
	* scaling here depends on removal of scaling
	* constants in windows plugin
	* 3dconnectionplugin.cpp:ndofEventHandler().
	* mac plugin is already correct
	*/

	if(dval[6] == 0) return; // guard against divide by zero

	for(i=0;i<6;i++) {

		// adjust for elapsed time since last report
		// this should be moved to device specific
		dval[i] = dval[i] / dval[6];

		// non-linear scaling
		if(dval[i]<0.0f)
			dval[i] = -1.0f * dval[i] * dval[i];
		else
			dval[i] = dval[i] * dval[i];

		// additional rotation scaling
		// (todo: add individual axis scaling)
		if(i>2)
			dval[i] = dval[i] / 25.0f;
	}


	/* low pass filter with zero crossing reset. */

	/* without zero crossing there is a ramp down
	* effect that could set up similar to fly()
	*/

	for(i=0;i<6;i++) {
		if((dval[i] * fval[i]) >= 0)
			dval[i] = (fval[i] * 15 + dval[i]) / 16;
		else
			fval[i] = 0;
	}


	/* Correct the distance jump if G.vd->dist != 0 */

	/* This is due to a side effect of the original
	* mouse view rotation code. The rotation point is
	* set a distance in front of the viewport to
	* make rotating with the mouse look better.
	* The distance effect is written at a low level
	* in the view management instead of the mouse
	* view function. This means that all other view
	* movement devices must subtract this from their
	* view transformations.
	*/

	upvec[0] = upvec[1] = upvec[2] = 0;
	if(G.vd->dist != 0.0) {
		odist = G.vd->dist;
		Mat3CpyMat4(mat, G.vd->viewinv);
		upvec[2]=G.vd->dist;
		Mat3MulVecfl(mat, upvec);
		VecSubf(G.vd->ofs, G.vd->ofs, upvec);
		G.vd->dist = 0.0;
	}


	/* Apply rotation */

	rvec[0] = -dval[3];
	rvec[1] = -dval[4];
	rvec[2] = dval[5];

	Mat3CpyMat4(mat, G.vd->viewinv);
	mat[2][2] = 0.0f;
	Mat3MulVecfl(mat, rvec);

	phi = Normalize(rvec);
	if(phi != 0) {
		VecRotToQuat(rvec,phi,q1);
		QuatMul(G.vd->viewquat, G.vd->viewquat, q1);
	}


	/* Apply translation */

	tvec[0] = dval[0];
	tvec[1] = dval[1];
	tvec[2] = -dval[2];

	// the next three lines rotate the x and y
	// translation coordinates by the current z axis angle

	Mat3CpyMat4(mat, G.vd->viewinv);
	mat[2][2] = 0.0f;
	Mat3MulVecfl(mat, tvec);

	VecSubf(G.vd->ofs, G.vd->ofs, tvec);


	/*----------------------------------------------------
	* refresh the screen
	*/

	scrarea_do_windraw(curarea);
	screen_swapbuffers();


	/* Update preview render window */

	BIF_view3d_previewrender_signal(curarea, PR_DBASE|PR_DISPRECT);
}


The scaling, rotation, and translation sections work well. The problem 
I'm having is with the removal of G.vd->dist mode. Without that section, 
the rotations occur out in front of the viewpoint instead of centered on 
the viewpoint. With that section included, the viewpoint rotates nicely 
about it's center. Unfortunately, I can't figure out how to correctly 
put G.vd->dist back the way it was. Orthographic and mouse moving are 
screwed up after using 6dof.

I spent the last week and a half trying to figure out what is going on 
with view transforms in general and the different view modes. It looks 
like I'll need a lot more time before I have even a basic understanding 
of how the 3dview is written. I have though learned a lot more about 
using blender, so time well spent.

I was hoping that the mouse specific dist, zoom/dolly, etc., attributes 
could be localized to mouse viewmove functions. This would allow other 
device viewmove functions to avoid having to compensate for them by 
first removing them, do their transformations, then add them back in 
(only to be removed again for the final windraw). This is the level of 
recode/refactor that would give long term benefits to the blender code base.

In the mean time I'll try to use the fly() function as the basis for an 
viewmoveNDOF() hack by swapping out its transform sections for the ones 
I wrote above. One problem with that is fly() does an internal loop 
during transformation that doesn't allow any other editing to occur 
while active. This seems to be necessary due to the problems with trying 
to compensate for G.vd->dist. It doesn't seem possible to do a fly mode 
transform on a frame by frame basis and allow editing too. The existing 
viewmoveNDOF() above allows editing objects and nodes with the mouse 
while at the same time flying through the scene. Although this isn't 
something necessary, I thought is was elegant that it was possible. If I 
can figure out how to keep that functionality, it may become the way you 
align your view while editing by providing small adjustments to the 
viewpoint to help line up what you are editing with other objects in the 
scene.

I hope I've stated well what I've been talking about the last few weeks. 
And, thanks guys for the opportunity to contribute to a better Blender.

SteveF




More information about the Bf-committers mailing list