Navigation widgets for modelspace

free scripts, plugins, models, textures
Post Reply
User avatar
clintonman
Captain
Posts: 5432
Joined: 21 May 2009, 21:08
Type the number ten into the box: 0
Location: California
Contact:

Navigation widgets for modelspace

Post by clintonman »

modelspaceWidgets.jpg
New widgets for object and point edit navigation in 2 tsx plugins. They are useful but flawed.
The object navigation widget works with meshes, lights and cameras and the point edit widget is for moving things around in PE mode. The widgets are useful, but a long way from perfect. The most important limitation is that the widgets do not maintain a set screen size and is unaware of the view orientation. This means that it will start to work backward when the view is rotated past a certain point and it naively reads the mouse x and y motion without any translation to 3D space. Another limitation is that a new widget is created for each object that is selected or point edited.

Find them about half way down this page:
http://www.clintons3d.com/plugins/trues ... index.html" onclick="window.open(this.href);return false;
Clinton Reese

http://clintons3d.com
User avatar
DesignDevil
Master Chief Petty Officer
Posts: 500
Joined: 22 May 2009, 08:52
Type the number ten into the box: 0
Location: Neuruppin, Germany, Earth.
Contact:

Re: Navigation widgets for modelspace

Post by DesignDevil »

Hi clintonman, seems to be that you have more effort to create the widgets that i want to create long time ago.

I do not testet your widgets but i read that you have not solved the problem with the mouse movement compared to the direction of the object in the view.

I had the same problem and i solved it in the following way. My widgets (move, rotate & scale) are also never finished because i run into a lot of problems.

The good news: The move widget works almost and i adapted this to create a widget just for moving MotionStudio Joints as a animation tool because these joints are never rotated or scaled - just moved and this widget works with one exception: i never finished the movement in 2 axis together in model space.

Why i never finished this project? Well, so far as i remember there was a issue to recognize if the user is in world or object space. To explain this: My widgets are shown directly in front of the camera projected to the parent object. That means the widget is always in front of you and never behind a other object. But it looks as is it in the axis center of hi parent object. The problem: this just works fine if you switch of the trueSpace cage. Otherwise the widget handling is quite stupid (don't ask me why - ask caligari).

To solve this i added my own cage (red OR blue lines on each corner of the object) so that the user can recognize the active object. One color was for object, the other for world space. This works fine so far but i could not find out if the user changes between world or object mode in trueSpace. The only solution for that would be to ignore trueSpace own buttons and just use the choose from the plugin. The same was for the choosen tool (move, rotate or scale). It is not possible (so far as i know) to find out which one is selected in trueSpace.

The other problems are (so far as i remember): the rotation widget was crazy and i never find out why - maybe the gimbal lock problem or a problem with trueSpace function to calculate rotations. The amount of movement, rotation or scaling in reference to the mouse movement and the choosen unit type was also a bit strange and i could not find out why. Sometimes movement feels good, sometimes (mostly if you zoom in) it feels strange - movement was to slow or to fast - can't remember.

Anyway - cool to see that there is one who try to create plugins for modelside. If you need more informations or my source - feel free to ask i can send it to you.

So here my method:

I use the "widget->DoNavDelta" callback to recognize that the user wants to move the widget (the object) in a desired axis. With this callback you got the m_delta_x and m_delta_y coordinates (mouse delta) since the last call. Well, the following is FreeBasic but i think you understand what i'm doing there.

Information:
- I also show a small borderless semitransparent window with the actual coordinates while dragging the widget ( ToolInfo window )
- Shift roundsup the actual position to 0.1 units

Code: Select all

'This callback is called when the mouse is moved with a pressed button. The appropriate action is derived from the material ID.
Function MyDoNavDelta Cdecl( objwdg As tsxWIDGET, materialid As CtsxMaterialID, m_delta_x As Integer, m_delta_y As Integer, m_flags As Integer ) As tsxErr
	
	Dim wdgParent As tsxGNode = tsxGNodeGetParent( objwdg )
	Dim objLoc As CtsxVector3f: tsxGNodeGetAxesPosition( wdgParent, objLoc )

	Dim As CtsxVector3f newPos, p2
	Dim As CtsxAxes3f axes
	Dim As Short xP1, yP1, xP2, yP2
	Dim As Single d
			
	Static As CtsxVector3f lastPos
	Static As Byte shiftFlag
	
	'get position
	tsxGNodeGetAxesPosition( wdgParent, newPos )

	'widget dragging action starts...
	If mouseClick = 0 Then
		'show tool info window
		ShowWindow( toolInfo, SW_SHOW )
		mouseClick = 1
		lastPos = newPos
	EndIf

	'overwrite position if shift key is pressed with stored value from last call
	If GetAsyncKeyState(VK_SHIFT) < -1 Then
		If shiftFlag Then newPos = lastpos
	EndIf
	
	'get object direction
	tsxGNodeGetAxesOrientation( wdgParent, axes )
	
	'compute object position to view plane
	tsxAViewGetScreenPoint( newPos, xP1, yP1 )
	p2 = newPos
	
	'the idea is this:
	
	'1. we project the current object position (newPos) onto the screen P1 = (xP1, yP1)
	'2. we calculate a second position in direction of the desired axes P2 = (xP2, yP2)
	'3. we project also this point onto the screen
	'4. if (p2 - p1) is POSITIVE, we ADD the mouse delta, if NEGATIVE we SUB the delta
	
	'Example: The object x-axis is pointing to the left on screen. That means a mouse
	'         move also to the left should increase the x position of the object,
	'         because we move in the object axis direction. Let's calculate this:
	'         p1, the object position, should be 400 on screen
	'         p2, the projected point, is 350 (or anything left from 400) on screen
	'         (p2 - p1) = -50
	'         The mouse delta is also negative (remember, we move to the left on screen)
	'         Our rule (4.) said we add the delta: (p2 - p1) + mouse_delta
	'         The mathematic rule said: neg + neg = pos
	'         As result we get a positive movement in the axis direction.
		
	'world or object mode
	If SceneCoordinateSystem = world Then
		
		'select move axis by materialID
		Select Case materialID
			'Z
			Case 0 
				p2.z += 1
				tsxAViewGetScreenPoint( p2, xP2, yP2 )
				If xP2 - xP1 > 0 Then newPos.z += m_delta_x / 100 Else newPos.z -= m_delta_x / 100
				If yP2 - yP1 > 0 Then newPos.z += m_delta_y / 100 Else newPos.z -= m_delta_y / 100
			'X
			Case 1
				p2.x += 1
				tsxAViewGetScreenPoint( p2, xP2, yP2 )
				If xP2 - xP1 > 0 Then newPos.x += m_delta_x / 100 Else newPos.x -= m_delta_x / 100
				If yP2 - yP1 > 0 Then newPos.x += m_delta_y / 100 Else newPos.x -= m_delta_y / 100
			'Y
			Case 2
				p2.y += 1
				tsxAViewGetScreenPoint( p2, xP2, yP2 )
				If xP2 - xP1 > 0 Then newPos.y += m_delta_x / 100 Else newPos.y -= m_delta_x / 100
				If yP2 - yP1 > 0 Then newPos.y += m_delta_y / 100 Else newPos.y -= m_delta_y / 100
			'XY
			Case 3
				Select Case tsxAViewGetViewMode()
					'perspective modes (3d or camera)
					Case e_tsxVM_PERSPECTIVE, e_tsxVM_CAMERA
						p2.x += 1
						tsxAViewGetScreenPoint( p2, xP2, yP2 )
						If xP2 - xP1 > 0 Then newPos.x += m_delta_x / 100 Else newPos.x -= m_delta_x / 100
						If yP2 - yP1 > 0 Then newPos.x += m_delta_y / 100 Else newPos.x -= m_delta_y / 100
						p2.x -= 1
						p2.y += 1
						tsxAViewGetScreenPoint( p2, xP2, yP2 )
						If xP2 - xP1 > 0 Then newPos.y += m_delta_x / 100 Else newPos.y -= m_delta_x / 100
						If yP2 - yP1 > 0 Then newPos.y += m_delta_y / 100 Else newPos.y -= m_delta_y / 100
					Case Else
						p2.x += 1
						p2.y += 1
						tsxAViewGetScreenPoint( p2, xP2, yP2 )
						If xP2 - xP1 > 0 Then newPos.x += m_delta_x / 100 Else newPos.x -= m_delta_x / 100
						If yP2 - yP1 > 0 Then newPos.y += m_delta_y / 100 Else newPos.y -= m_delta_y / 100
				End Select
			'XZ
			Case 4
				Select Case tsxAViewGetViewMode()
					'perspective modes (3d or camera)
					Case e_tsxVM_PERSPECTIVE, e_tsxVM_CAMERA
						p2.x += 1
						tsxAViewGetScreenPoint( p2, xP2, yP2 )
						If xP2 - xP1 > 0 Then newPos.x += m_delta_x / 100 Else newPos.x -= m_delta_x / 100
						If yP2 - yP1 > 0 Then newPos.x += m_delta_y / 100 Else newPos.x -= m_delta_y / 100
						p2.x -= 1
						p2.z += 1
						tsxAViewGetScreenPoint( p2, xP2, yP2 )
						If xP2 - xP1 > 0 Then newPos.z += m_delta_x / 100 Else newPos.z -= m_delta_x / 100
						If yP2 - yP1 > 0 Then newPos.z += m_delta_y / 100 Else newPos.z -= m_delta_y / 100
					Case Else
						p2.x += 1
						p2.z += 1
						tsxAViewGetScreenPoint( p2, xP2, yP2 )
						If xP2 - xP1 > 0 Then newPos.x += m_delta_x / 100 Else newPos.x -= m_delta_x / 100
						If yP2 - yP1 > 0 Then newPos.z += m_delta_y / 100 Else newPos.z -= m_delta_y / 100
				End Select
			'YZ
			Case 5
				Select Case tsxAViewGetViewMode()
					'perspective modes (3d or camera)
					Case e_tsxVM_PERSPECTIVE, e_tsxVM_CAMERA
						p2.y += 1
						tsxAViewGetScreenPoint( p2, xP2, yP2 )
						If xP2 - xP1 > 0 Then newPos.y += m_delta_x / 100 Else newPos.y -= m_delta_x / 100
						If yP2 - yP1 > 0 Then newPos.y += m_delta_y / 100 Else newPos.y -= m_delta_y / 100
						p2.y -= 1
						p2.z += 1
						tsxAViewGetScreenPoint( p2, xP2, yP2 )
						If xP2 - xP1 > 0 Then newPos.z += m_delta_x / 100 Else newPos.z -= m_delta_x / 100
						If yP2 - yP1 > 0 Then newPos.z += m_delta_y / 100 Else newPos.z -= m_delta_y / 100
					Case Else
						p2.y += 1
						p2.z += 1
						tsxAViewGetScreenPoint( p2, xP2, yP2 )
						If xP2 - xP1 > 0 Then newPos.y += m_delta_x / 100 Else newPos.y -= m_delta_x / 100
						If yP2 - yP1 > 0 Then newPos.z += m_delta_y / 100 Else newPos.z -= m_delta_y / 100
				End Select
		End Select
		
	Else
		
		'select move axis by materialID
		Select Case materialID
			'Z
			Case 0
				tsxAddVec3f( p2, axes.zaxis )
				tsxAViewGetScreenPoint( p2, xP2, yP2 )
				If xP2 - xP1 > 0 Then d += m_delta_x / 100 Else d -= m_delta_x / 100
				If yP2 - yP1 > 0 Then d += m_delta_y / 100 Else d -= m_delta_y / 100
				tsxMulScalar3f( axes.zaxis, d )
				tsxAddVec3f( newPos, axes.zaxis )
			'X
			Case 1
				tsxAddVec3f( p2, axes.xaxis )
				tsxAViewGetScreenPoint( p2, xP2, yP2 )
				If xP2 - xP1 > 0 Then d += m_delta_x / 100 Else d -= m_delta_x / 100
				If yP2 - yP1 > 0 Then d += m_delta_y / 100 Else d -= m_delta_y / 100
				tsxMulScalar3f( axes.xaxis, d )
				tsxAddVec3f( newPos, axes.xaxis )
			'Y
			Case 2
				tsxAddVec3f( p2, axes.yaxis )
				tsxAViewGetScreenPoint( p2, xP2, yP2 )
				If xP2 - xP1 > 0 Then d += m_delta_x / 100 Else d -= m_delta_x / 100
				If yP2 - yP1 > 0 Then d += m_delta_y / 100 Else d -= m_delta_y / 100
				tsxMulScalar3f( axes.yaxis, d )
				tsxAddVec3f( newPos, axes.yaxis )
		End Select
		
	EndIf

	'store for shift key
	lastPos = newPos 

	'round up position if shift key is pressed to 0.1 units
	If GetAsyncKeyState(VK_SHIFT) < -1 Then
		shiftFlag = TRUE
		newPos.z = Round( newPos.z * 10 ) / 10
		newPos.x = Round( newPos.x * 10 ) / 10
		newPos.y = Round( newPos.y * 10 ) / 10
	Else
		shiftFlag = FALSE
	EndIf

	'set the new position
	tsxGNodeSetLocation( wdgParent, newPos )
			
	'refresh selected object in all views
	tsxAllViewRefreshSelObject()

	Return tsxERR_SUCCESS
	
End Function
I still use TrueSpace 6.6
Link: My Youtube Channel
Link: www.DesignDevil.de - Plugins, Tutorials and more about TrueSpace 6.6
User avatar
clintonman
Captain
Posts: 5432
Joined: 21 May 2009, 21:08
Type the number ten into the box: 0
Location: California
Contact:

Re: Navigation widgets for modelspace

Post by clintonman »

Thanks, Design Devil.
The tsxAViewGetScreenPoint was the key. I've only tested it in the x axis but I got it to work no matter what the eye orientation. This is what I came up with. The case for move in x calls the function below. It uses the position of the object and a position (1,0,0) in the x direction to get the direction of the x axis. It uses the tsxAViewGetScreenPoint function to translate those positions to screen space and gets the angle between the vector formed by those points and the mouse movement.

Code: Select all

float GetMoveX(tsxWIDGET *objwdg,int m_delta_x, int m_delta_y)
{
	float mouseXscaled = (float)m_delta_x*0.005f;
	float mouseYscaled = (float)m_delta_y*0.005f;
	float mouseMagnitudeScaled = sqrt(mouseXscaled*mouseXscaled + mouseYscaled*mouseYscaled);
	float mouse_delta_x = (float)m_delta_x;
	float mouse_delta_y = (float)m_delta_y;
	short centerX, centerY, xdirScreenX, xdirScreenY;
	CtsxTxmx3f   txmx;
	CtsxVector3f xdir = { 1.0f, 0.0f, 0.0f };
	CtsxVector3f center = { 0.0f, 0.0f, 0.0f };

	//get parent polyhedron
	tsxPOLYHEDRON *wdgspolyparent = (tsxPOLYHEDRON *)tsxGNodeGetParent((tsxGNODE*)objwdg);

	//get position of the object
	tsxGNodeGetAxesPosition((tsxGNODE*)wdgspolyparent, &center);
	
	//if modelframe transform the x direction to world
	if(g_frame == e_tsxModelFrame)
	{
		tsxPolyhGetVxTxmx(wdgspolyparent, &txmx);
		tsxRotateVect3f(xdir, xdir, txmx);
	}

	//screen position of object
	tsxAViewGetScreenPoint(&center, &centerX, &centerY);
	//add to the direction vector
	tsxAddVec3f(xdir, center);

	//screen position of x direction vector
	tsxAViewGetScreenPoint(&xdir, &xdirScreenX, &xdirScreenY);

	//xdirection is screen x axis direction - center of polyhedron
	float xdirectionX, xdirectionY;
	xdirectionX = (float)(xdirScreenX - centerX);
	xdirectionY = (float)(xdirScreenY - centerY);
	//normalize
	float mag,mousemag,mousex,mousey,angle;
	mag = sqrt(xdirectionX*xdirectionX + xdirectionY*xdirectionY);
	mousemag = sqrt(mouse_delta_x*mouse_delta_x + mouse_delta_y*mouse_delta_y);
	xdirectionX = xdirectionX/mag;
	xdirectionY = xdirectionY/mag;
	mousex = mouse_delta_x/mousemag;
	mousey = mouse_delta_y/mousemag;
	angle = atan2(mousey ,mousex) - atan2(xdirectionY, xdirectionX);
	//check for > 2 pi and adjust
	if(angle > M_PI) 
		angle = angle - 2.0f * M_PI;
	if(angle < -M_PI) 
		angle = angle + 2.0 * M_PI;
	if(angle > 0 && angle < M_PI/2.0f) 
		return mouseMagnitudeScaled;
	if(angle < 0 && angle > -M_PI/2.0f) 
		return  mouseMagnitudeScaled;
	if(angle > M_PI/2.0f && angle < M_PI) 
		return  -mouseMagnitudeScaled;
	if(angle < -M_PI/2.0f && angle > -M_PI) 
		return -mouseMagnitudeScaled;
	return 0.0f;
}
Clinton Reese

http://clintons3d.com
User avatar
clintonman
Captain
Posts: 5432
Joined: 21 May 2009, 21:08
Type the number ten into the box: 0
Location: California
Contact:

Re: Navigation widgets for modelspace

Post by clintonman »

Update:
Widgets are now mostly aware of the view - a lot less backward motions
Improved widget tooltips/help
PE widget will use the "selection frame" instead of the object frame, so it lines up with the selection instead of the object. This makes the motion more predictable, but the widget placement is probably worse than before.
Clinton Reese

http://clintons3d.com
User avatar
BNG
Chief Warrant Officer
Posts: 684
Joined: 16 Oct 2009, 03:26
Type the number ten into the box: 0
Location: Texas
Contact:

Re: Navigation widgets for modelspace

Post by BNG »

Very cool and thank you Clinton. I love to see that TSX based plugin development is still quite alive. I absolutely love the modelside of 7.61 and consider it far from obsolete. I still grapple with the newer workspace side, but do use it in conjunction with the modelside where newer model formats are needed. I hope that evetually both sides are fully integrated together making it simpler to switch back and forth as the model maker creates new things. Leroy.
Post Reply