Skip to main content
GameDev.net gamedev.net
🔒 Locked

Tool development question

Started by Little Coding Fox Sep 10, 2006 at 10:36 AM 3 replies 1.1k views
Original Post
Little Coding Fox
Little Coding Fox
Greets everyone. I've been needing to create map/model editors and other tools for my game engine, but i dont know where to look information at on how to develop such tools. I tried google, but i didnt find much info, as only products of enterprises showed up... Would you guys mind helping me out? Thank you for your help. EDIT: Forgot to say, using C++ and OpenGL.
s3mt3x
s3mt3x
Hi nuno_silva_pt,

I've recently finished an editor and I can give you a few tips.

1. The most important thing is selection with the mouse. You should google for "OpenGL selection" to start with. Opengl has an in built selection mechanism, but i found it easier to render my primatives in a particular color (shading, lighting & textureing disabled), read a pixel back from the frame buffer (at the mouse position) then you have the primative you clicked on (based on the color).

2. Once you've got selection up and running, then you need to start thinking about tools. In my app, I renderted all the tool ui (i.e. arrows for the translate tool) using opengl, then I used the same selection routines to see if a tool handle was hit before checking for other primatives - if the handle was hit - then pass the mouse events to the tool - if not carry on looking for objects.

Once you've got selection up and running, writing code for each tool is relatively easy - I would make sure you have a good Plane class up and running as for eaxample - a transform tool I did the following:

1. Find the object's center.
2. If transform handle is selected
Create a plane facing the camera
Intersect a ray from the eye into the plane at X, Y - save the position of intersection
When the mouse is dragged
Intersect the plane again at the new mouse position
subtract the old position from the new one
Transform the primative by the resulting vector.

this allows you to get a wyswyg transfomr tool very easily.

(nothing worse than trying to darg an object and it flies off when it should always be under the mouse).

Hope this helps,

Kind Regards,

Chris

Forgot to say - first you'll need a decent camera to be able to navigate around - based mine on maya - i've popped the code below:

#include "stdafx.h"#include "Vector3.h"#include "Matrix.h"#include "Plane.h"#include "Scene.h"class PerspectiveCamera{public:	PerspectiveCamera(Scene * scene);	~PerspectiveCamera(void);public:	//main view method	void View();	//mouse handlers	void LeftMouseDown(int x, int y);	void LeftMouseUp(int x, int y);	void MiddleMouseDown(int x, int y);	void MiddleMouseUp(int x, int y);	void RightMouseDown(int x, int y);	void RightMouseUp(int x, int y);	void MouseMove(int x, int y);	//resize	void Resize(int width, int height);	//view methods	Vector3 IntersectPlaneFromCameraPosition(Vector3 planeNormal, Vector3 pointOnPlane, int x, int y);	Vector3 GenerateNormalizedPickRay(int x, int y);	Vector3 GetCameraPosition();	Vector3 GetHitPoint(int x, int y);private:	//viewport variables	int viewPortWidth;	int viewPortHeight;	//mouse variables	bool leftMouseDown;	bool middleMouseDown;	bool rightMouseDown;	int mouseX;	int mouseY;	Plane mouseDragPlane;	//local axis	Vector3 up;	Vector3 right;	Vector3 forward;	//position	Vector3 translation;	//zoom	float zoom;	//rotation	float rotationX;	float rotationY;	Scene * scene;private:	void UpdateCameraAxis();};


#include ".\perspectivecamera.h"#include ".\GLStuff.h"PerspectiveCamera::PerspectiveCamera(Scene * scene){	leftMouseDown = false;	middleMouseDown = false;	rightMouseDown = false;	up = Vector3(0, 1, 0);	right = Vector3(1, 0, 0);	forward = Vector3(0, 0, 1);	translation = Vector3(0, 0, 0);	rotationX = 0;	rotationY = 0;	zoom = 1000;	mouseX = 0;	mouseY = 0;	this->scene = scene;}PerspectiveCamera::~PerspectiveCamera(void){}void PerspectiveCamera::View() {	glViewport(0, 0, viewPortWidth, viewPortHeight);	glMatrixMode(GL_PROJECTION);	glLoadIdentity();	gluPerspective(45.0f,(float)viewPortWidth/(float)viewPortHeight,1.0f,10000.0f);	glMatrixMode(GL_MODELVIEW);	glLoadIdentity();	//UpdateCameraAxis();	gluLookAt(0, 0, zoom, 0, 0, 0, 0, 1, 0);				glRotatef(rotationX, 1.0f, 0.0f, 0.0f);	glRotatef(rotationY, 0.0f, 1.0f, 0.0f);	glTranslatef(translation.x, translation.y, translation.z);}void PerspectiveCamera::UpdateCameraAxis() {	Matrix mY;	mY.Rotate(-rotationY, 0, 1, 0);	Matrix mX;	mX.Rotate(-rotationX, 1, 0, 0);	Matrix m = mY * mX;	Vector3 newUp(0, 1, 0);	Vector3 newRight(1, 0, 0);	Vector3 newForward(0, 0, 1);	m.TransformVector(newUp);	m.TransformVector(newRight);	m.TransformVector(newForward);	newUp.Normalize();	newRight.Normalize();	newForward.Normalize();	up = newUp;	right = newRight;	forward = newForward;}void PerspectiveCamera::Resize(int width, int height) {	viewPortWidth = width;	viewPortHeight = height;}Vector3 PerspectiveCamera::GenerateNormalizedPickRay(int x, int y) {		View();	double modelView[16];	double projection[16];	int viewPort[4];	glGetDoublev(GL_MODELVIEW_MATRIX, modelView);	glGetDoublev(GL_PROJECTION_MATRIX, projection);	glGetIntegerv(GL_VIEWPORT, viewPort);	viewPort[0] = 0;	viewPort[1] = 0;	y = viewPort[3] - y;	double nearX, nearY, nearZ;	double farX, farY, farZ;	gluUnProject(x, y, 0.0f, modelView, projection, viewPort, &nearX, &nearY, &nearZ);	gluUnProject(x, y, 1.0f, modelView, projection, viewPort, &farX, &farY, &farZ);	Vector3 direction = Vector3((float) (nearX - farX), (float) (nearY - farY), (float) (nearZ - farZ));	direction.Normalize();	return direction;}	Vector3 PerspectiveCamera::GetHitPoint(int x, int y) {	//make sure we've got the current view matrix	View();	float distance = zoom;			float value_fov = 0.7853f;	//this is 45 degrees converted to radians	float value_aspect = ((float) viewPortWidth) / ((float) viewPortHeight);	//the aspect ratio for a 640 x 480 window	float half_window_width = viewPortWidth / 2.0f;	float half_window_height = viewPortHeight / 2.0f;	//set to half of your window width	//here i've set it to 320 - half of 640	//same for window height	float modifier_x;	float modifier_y;	//mathematical handling of the difference between	//your mouse position and the 'center' of the window	float point[3];	//the untransformed ray will be put here	float point_dist = distance;	//it'll be put this far on the Z plane	float camera_origin[3];	//this is where the camera sits, in 3dspace	float point_xformed[3];	//this is the transformed point	float pulledMatrix[16];	glGetFloatv(GL_MODELVIEW_MATRIX, pulledMatrix);	//pulledMatrix is the OpenGL matrix.	float final_point[3];	//These lines are the biggest part of this function.	//This is where the mouse position is turned into a mathematical	//'relative' of 3d space. The conversion to an actual point	modifier_x = ((float) tan( value_fov * 0.5f )) * (( 1.0f - x / half_window_width ) * ( value_aspect ) );	modifier_y = ((float) tan( value_fov * 0.5f )) * -( 1.0f - y / half_window_height );	//These 3 take our modifier_x/y values and our 'casting' distance	//to throw out a point in space that lies on the point_dist plane.	//If we were using completely untransformed, untranslated space,	//this would be fine - but we're not :)	point[0] = modifier_x * point_dist;	point[1] = modifier_y * point_dist;	point[2] = point_dist;	//Next we make an openGL call to grab our MODELVIEW_MATRIX - 	//This is the matrix that rasters 3d points to 2d space - which is	//kinda what we're doing, in reverse	//Gl.glGetFloatv(GL.GL_MODELVIEW_MATRIX, pulledMatrix, 16);	//Some folks would then invert the matrix - I invert the results.	//First, to get the camera_origin, we transform the 12, 13, 14	//slots of our pulledMatrix - this gets us the actual viewing	//position we are 'sitting' at when the function is called	camera_origin[0] = -(		pulledMatrix[0] * pulledMatrix[12] + 		pulledMatrix[1] * pulledMatrix[13] + 		pulledMatrix[2] * pulledMatrix[14]);	camera_origin[1] = -(		pulledMatrix[4] * pulledMatrix[12] + 		pulledMatrix[5] * pulledMatrix[13] + 		pulledMatrix[6] * pulledMatrix[14]);	camera_origin[2] = -(		pulledMatrix[8] * pulledMatrix[12] + 		pulledMatrix[9] * pulledMatrix[13] + 		pulledMatrix[10] * pulledMatrix[14]);	//Second, we transform the position we generated earlier - the '3d'	//mouse position - by our viewing matrix.	point_xformed[0] = -(		pulledMatrix[0] * point[0] + 		pulledMatrix[1] * point[1] + 		pulledMatrix[2] * point[2]);	point_xformed[1] = -(		pulledMatrix[4] * point[0] + 		pulledMatrix[5] * point[1] + 		pulledMatrix[6] * point[2]);	point_xformed[2] = -(		pulledMatrix[8] * point[0] + 		pulledMatrix[9] * point[1] + 		pulledMatrix[10] * point[2]);	//That's pretty much that. Using the camera origin and the	//transformed ray, you can now do ray-triangle collision	//detection, which i'll leave up to you.	//so you can see this function in action, i've included a second	//function to draw a sprite (billboard)	final_point[0] = point_xformed[0] + camera_origin[0];	final_point[1] = point_xformed[1] + camera_origin[1];	final_point[2] = point_xformed[2] + camera_origin[2];	return Vector3(final_point[0], final_point[1], final_point[2]);}Vector3 PerspectiveCamera::GetCameraPosition() {	//make sure we've got the current view loaded	View();	float camera_origin[3];	//this is where the camera sits, in 3dspace	float pulledMatrix[16];	//pulledMatrix is the OpenGL matrix.	//Next we make an openGL call to grab our MODELVIEW_MATRIX - 	//This is the matrix that rasters 3d points to 2d space - which is	//kinda what we're doing, in reverse	glGetFloatv(GL_MODELVIEW_MATRIX, pulledMatrix);	//Some folks would then invert the matrix - I invert the results.	//First, to get the camera_origin, we transform the 12, 13, 14	//slots of our pulledMatrix - this gets us the actual viewing	//position we are 'sitting' at when the function is called	camera_origin[0] = -(		pulledMatrix[0] * pulledMatrix[12] + 		pulledMatrix[1] * pulledMatrix[13] + 		pulledMatrix[2] * pulledMatrix[14]);	camera_origin[1] = -(		pulledMatrix[4] * pulledMatrix[12] + 		pulledMatrix[5] * pulledMatrix[13] + 		pulledMatrix[6] * pulledMatrix[14]);	camera_origin[2] = -(		pulledMatrix[8] * pulledMatrix[12] + 		pulledMatrix[9] * pulledMatrix[13] + 		pulledMatrix[10] * pulledMatrix[14]);	return Vector3(camera_origin[0], camera_origin[1], camera_origin[2]);}void PerspectiveCamera::LeftMouseDown(int x, int y) {	leftMouseDown = true;	mouseX = x;	mouseY = y;}void PerspectiveCamera::LeftMouseUp(int x, int y) {	leftMouseDown = false;	mouseX = x;	mouseY = y;}void PerspectiveCamera::MiddleMouseDown(int x, int y) {	middleMouseDown = true;	mouseX = x;	mouseY = y;}void PerspectiveCamera::MiddleMouseUp(int x, int y) {	middleMouseDown = false;	mouseX = x;	mouseY = y;}void PerspectiveCamera::RightMouseDown(int x, int y) {	rightMouseDown = true;	mouseX = x;	mouseY = y;}void PerspectiveCamera::RightMouseUp(int x, int y) {	rightMouseDown = false;	mouseX = x;	mouseY = y;}void PerspectiveCamera::MouseMove(int x, int y) {	if (leftMouseDown) 	{		float deltaX = x - mouseX;		float deltaY = y - mouseY;		float rotationWidthFactor = 270.0f / (viewPortWidth / 2.0f);		float rotationHeightFactor = 270.0f / (viewPortHeight / 2.0f);		rotationX += (deltaY * rotationHeightFactor);		rotationY += (deltaX * rotationWidthFactor);		//this.frustum.Reset(this);	} 	else if (rightMouseDown) 	{		int deltaY = y - mouseY;		zoom -= ((float) deltaY) * (zoom / 1000.0f);		//this.frustum.Reset(this);	}	else if (middleMouseDown) 	{		Vector3 newPosition = GetHitPoint(x, y);		Vector3 currentPosition = GetHitPoint(mouseX, mouseY);		Vector3 difference = newPosition - currentPosition;		translation.x += difference.x;		translation.y += difference.y;		translation.z += difference.z;		//this.frustum.Reset(this);	}	mouseX = x;	mouseY = y;	if (leftMouseDown || middleMouseDown || rightMouseDown) 	{			this->scene->Draw();	}}
NIm
NIm
Why do you need a seperate model editor?

For a map editor, I would take an open source model editor for the terrain, and add functionality for inserting objects from your game and displaying them.

Alternatively, you could use the game engine and create routines and state for pausing everything and creating things. For this, look at a game called Sauerbraten
Xetick
Xetick
First you need to decide if you want one tool or many tools. On the latest GDC rountable we concluded that one tool that does everything is best if your a single tool developer. If there is a few of you it's better to split them up.

Next up is that you should try to keep a good flow in a tool. That is never have any modal dialog boxes. They stop the flow of the user because you force them to respond and that takes away from their focus. A good example is visual studio where they have their docking system. Lots of windows but you dont have to care about them as such.

Also in these days for a tool I would go with C#. It's a lot easier do create the gui in C# than it is in MFC. I cut my tool development time in about half if not more just thanks to moving from C++ to C# for my tools. Also in C# you can use the free DockPanel to get it to look like Visual Studio. In other words a familar look. Another thing that's good in tool development. Don't invent new controls and other things unless you must do it. Users are familar with how windows controls work. They don't want to learn a new strange control just because some tool programmer feelt like it was fun to create it's own version of a slider.

The thing you need to keep in mind is that a tool is something a user DONT want to use. They dont want to use your map editor. They just want their map to be done. So the less you hinder the user or force him to learn new things he doesn't know or actually care about to known the better.

Hmm.. I had a few more things but of course I forgot them now. Anyway it's a start.
Plane9 - Home of the free scene based music visualizer and screensaver
JohnRenstrom
JohnRenstrom
I agree...breaking tools up into smaller tools is the best approach. I have seen it done both ways and having a single huge tool can be quite dangerous. For example, you try to fix a bug related to one thing that can often cause something else to break. It especially gets tricky if more than one team is trying to use the tool. I have seen weeks of artist work lost because a bug was introduced in an obscure section of the tool.

Using smaller adhoc tools within a nice framework will always be your best solution. Also, whatever your framwork be, be sure to always make it your goal to support your artists and content creators. They don't like performing technical processes and can often make a lot of human errors leading to more time the programmers have to spend finding that bad peice of data. The optimal framwork would mask all the technical complexities from them.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.