Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Results 1 to 3 of 3

Thread: Mouse Picking? with my Game Machine

  1. #1
    Junior Member Newbie
    Join Date
    May 2012
    Posts
    12

    Unhappy Mouse Picking? with my Game Machine

    Hi,

    I need help with what im doing wrong with my mouse picking. Getting the required info from the cursors x,y pixels, to building a ray, to intersection with AABB's ive spent quite abit of time on this and im not great at maths and this seems to be very hard so some help would great. Also you might be interested in checking out The Foxy Game Machine for future reference.

    When i rotate the camera it messes up, it sortof works for non-rotations but then i have problems with AABB detection. The entire project has been recently released under license at www.fobbix.com/projects.php and www.fobbix.com/projects/foxy_game_machine.php. The important files to be viewed for this problem are.. In Template Project - Game.cpp (Rotation wise), In FoxyEngine - MathGL.cpp, Math.cpp, Map.cpp (PickTile) and Detect.cpp (GetPickRay) along with there .h files. I could list the sourcecode here but since ive released it there i dont really see the point.

    And also ive done quite abit with networking - berkeley sockets TCP/IP and UDP/IP , and generally i need to know how to interrupt a thread though? - even though not apparent here but id thought id ask anyway.

    Thanks,

    David

  2. #2
    Junior Member Newbie
    Join Date
    May 2012
    Posts
    12
    nobody has an answer?, do i need to post my sourcecode here?

  3. #3
    Junior Member Newbie
    Join Date
    May 2012
    Posts
    12
    Heres the code anyway..

    //apply rotation in startup
    Code :
    			//set camera
    			//camera pos needs to be half of screen
    			e_camera->setFOV(90.0f);
    			e_camera->setNear(0.1f);
    			e_camera->setFar(100.0f);
    ///should be doing this in camera class
    			e_engine->setPerspective(true);
    			e_camera->setPerspective();
    			//e_camera->setOrtho(&pos, &size);
    			//e_camera->setDirection(RADIANS(45.0f), -0.5f, 0.0f, 1.0f);
    			//e_camera->setPosition(20.0f, 8.0f, -2.5f);
    			//e_camera->ApplyTransform(FOXY_TRANSLATION);
    			//e_camera->Rotate(RADIANS(-45.0f), 0.0f, 0.0f, 1.0f);
    			//e_camera->ApplyTransform(FOXY_ROTATION);
    			e_camera->Rotate(RADIANS(-20.0f), 1.0f, 0.0f, 0.0f);
    			e_camera->ApplyTransform(FOXY_ROTATION);

    //GetPickRay
    Code :
    	void Detect::GetPickRay(Vector3 *pos, Vector3 *dir, float sx, float sy, float fov, float width, float height)
    	{
    		float d, aspect;
    		Vector3 vp, vpos, poss;
    		Vector3 unit_rot, rot;
    		Vector3 *cpos;
     
    		d = 1.0f / tanf(fov * PI / 360.0f);
    		aspect = width / height;
     
    		//get clip space
    		vp.x = 2.0f * aspect * sx / width - aspect; //could try testing the aspect after but seems ok (with paper)
    		vp.y = 2.0f * sy / height - 1.0f; //from -2.0f and + 1.0f since we invert our pixel position
    		vp.z = -d;
     
    		//transform point into world space
    		e_math_gl->TransformPointByMatrix(&vpos, e_camera->m_invert_mv_space, &vp);
     
    		//pos from camera eye
    		cpos = e_camera->getPosition(); //local positions not from matrix (saved a vec3)
     
    		pos->x = (-cpos->x);
    		pos->y = (-cpos->y);
    		pos->z = (-cpos->z);
     
    		//get rotation
    		/*static float rot_space[9];
    		e_math_gl->ExtractRotationMatrix(rot_space, e_camera->m_model_space);//e_camera->m_rotation_space);
    		e_math_gl->getFixedAnglesVector(rot_space, &rot);
    		e_math->Normal(&rot, &unit_rot);*/
     
    		//doesnt work when rotating camera, or is this just a visual artefact? (for rendering the test since i add 0.1 in Z) if so then AABB - Ray Intersection isnt working?
    //without rotation AABB - ray only works for the bottom half of the screen, even though the pickray appears to be working correctly (and i can move the screen around without problems)
    		e_math->Normal(&vp, dir);
    	}

    //PickTile
    Code :
    	int Map::PickTile(int x, int y)
    	{
    		/*static float offset;
    		static sfuMapTilePickingClosest data_new, data_old;
    		static Vector3 point_on_ray, ray_origin;
     
    ///this might be wrong
    		//first get the original ray coordinates (camera position + cursor offset)
    		/*if (x > 0)
    			offset = (2 * (x - e_engine->m_map_space[12])) / e_engine->wnd_size.x - 1;
    		else
    			offset = 0;
     
    		ray_origin.x = m_cam_coords->x + offset;
     
    		if (y > 0)
    			offset = (2 * (y - e_engine->m_map_space[13])) / e_engine->wnd_size.y - 1;
    		else
    			offset = 0;
     
    		ray_origin.y = m_cam_coords->y + offset;
    		ray_origin.z = m_cam_coords->z; //might need to inverse this?
     
    ///need to also store here the current angle of the camera
    ///could try and get away with using the radians m_angle thingy here
    ///could also pass that directly into the function
    		//then get the current rotation of the camera
     
    		///can just shove the first tile into data_old
    		/*data_old.dist = e_math_gl->ClosestPointOnRay(&point_on_ray, &ray_origin, m_cam_direction, &(m_tile_coords[(int)((m_cam_coords->y * m_width) + m_cam_coords->x)]));
    		data_old.tile = (int)((m_cam_coords->y * m_width) + m_cam_coords->x);
     
    		for (i = m_cam_coords->y; i < m_cam_view->x; i++)
    		{
    			for (o = m_cam_coords->x; o < m_cam_view->y; o++)
    			{
    				data_new.dist = e_math_gl->ClosestPointOnRay(&point_on_ray, 
    															 &ray_origin, 
    															 m_cam_direction, 
    															 &(m_tile_coords[(int)((i * m_width) + o)]));
    				if (data_new.dist < data_old.dist)
    				{
    					data_old.dist = data_new.dist;
    					data_old.tile = (int)((i * m_width) + o);
    				}
    			}
    		}*/
     
    		static Vector3 unit_ray, ray, pos, endray, point;
    		static float distance;
     
    		m_hover_bool_render = false;
     
    //mainly just need the position could get the direction anyway, and that should be just
    //x/y/z * w? but meh, could put the position at the center of the camera aswell, which is what we want anyway
    //obv not doing this very good, remember ray is just the direction
    		/* get tile from cursor position via picking */
    		e_detect->GetPickRay(&pos, &unit_ray, x, y, e_camera->getFOV(), e_engine->wnd_size.x, e_engine->wnd_size.y);
     
    		memcpy(&ray, &unit_ray, sizeof(Vector3));
     
    		//need to get the length for the ray, (times by a scalar in this case distance)
    		e_math->MultiplyVectorByScalar(&ray, e_camera->getFar());
    		m_endpos.x = ray.x + pos.x;
    		m_endpos.y = ray.y + pos.y;
    		m_endpos.z = ray.z + pos.z;
     
    #ifdef _DEBUG
    		memcpy(&m_startpos, &pos, sizeof(Vector3));
    		m_startpos.z -= 0.1f;
     
    		//debug/render pick ray
    		if (m_debug_first == true)
    		{
    			e_sb->Begin(GL_QUADS, 4, NULL);
    				/*e_sb->AddPos(4.0f, 4.0f, -1.0f);
    				e_sb->AddPos(4.1f, 4.0f, -1.0f);
    				e_sb->AddPos(4.1f, 8.0f, -1.0f);
    				e_sb->AddPos(4.0f, 8.0f, -1.0f);*/
     
    ///need to rotate these
    				e_sb->AddPos(m_startpos.x, m_startpos.y, m_startpos.z);
    				e_sb->AddPos(m_startpos.x + 0.1f, m_startpos.y, m_startpos.z);
    				e_sb->AddPos(m_endpos.x, m_endpos.y, m_endpos.z);
    				e_sb->AddPos(m_endpos.x + 0.1f, m_endpos.y, m_endpos.z);
    			m_debug_sb = e_sb->End();
    			m_debug_first = false;
    		}
    		else //modifying positions
    		{
    			e_sb->ReplacePos(m_debug_sb, 0, m_startpos.x, m_startpos.y, m_startpos.z);
    			e_sb->ReplacePos(m_debug_sb, 1, m_startpos.x + 0.1f, m_startpos.y, m_startpos.z);
    			e_sb->ReplacePos(m_debug_sb, 2, m_endpos.x, m_endpos.y, m_endpos.z);
    			e_sb->ReplacePos(m_debug_sb, 3, m_endpos.x + 0.1f, m_endpos.y, m_endpos.z);
    		}
    #endif
    		static float t;
     
    		//check all map but filter later to the size
    		//or 1000 boxes
    		for (i = 0; i < m_length; i++)
    		{
    			//first get the closest point on ray then scale to that ray
    			/*memcpy(&point, &m_endpos, sizeof(Vector3));
    			t = e_math_gl->ClosestPointOnRay(&point, &pos, &unit_ray, &(m_bbox[i].min));
     
    			///would project to inside the box (might need max and get the middle to calculate)
    			//e_math_gl->ClosestRayOnRay(&endray, &pos, &unit_ray, &(m_bbox[i].min));
    			//t = e_math->DistanceSquared3(&pos, &(m_bbox[i].min));
     
    			memcpy(&endray, &unit_ray, sizeof(Vector3));
    			endray.x *= t;
    			endray.y *= t;
    			endray.z *= t;*/
     
    			//just need to see whether distance is correct?
    			//memcpy(&ray, &unit_ray, sizeof(Vector3));
    			//e_math->MultiplyVectorByScalar(&ray, e_camera->getFar());
     
    			//first to collide
    			if (e_detect->BoxIntersectRay(&(m_bbox[i].min), &(m_bbox[i].max), &pos, &unit_ray) == true)
    			{
    				// select this one
     
    				int type;
     
    				///the selection should be based on what we current have in the in tile, so need to keymap that and then set the highlight
    				/*switch (m_in_tile.sprite_type)
    				{
    				case FOXY_SPRITE_ACTOR:
    					type = i_actor->getType(m_in_tile.sprite_pos);
    					m_hover_width = m_keymap_actors[type].width;
    					m_hover_height = m_keymap_actors[type].height;
    				break;
     
    				case FOXY_SPRITE_BUILDING:
    					//type = i_building->getType(m_in_tile.sprite_pos);
    					m_hover_width = m_keymap_buildings[type].width;
    					m_hover_height = m_keymap_buildings[type].height;
    				break;
     
    				case FOXY_SPRITE_OBJECT:
    					//type = i_object->getType(m_in_tile.sprite_pos);
    					m_hover_width = m_keymap_objects[type].width;
    					m_hover_height = m_keymap_objects[type].height;
    				break;
    				}*/
     
    				m_hover_width = 1;
    				m_hover_height = 1;
     
    				//store records in hover tile for rendering puposes and return the tile
    				m_hover_tile_pos = i;
     
    				m_hover_bool_render = true;
     
    				return i;
    			}
    		}
     
    		//loose, no tile selected
    		return FOXY_INVALID;
    	}

    //Math
    Code :
    	void MathGL::InvertMatrix(float *inverse, float *in_m)
    	{
    		int i, j;
    		float det, detij;
     
    		// calculate 4x4 determinant
    		det = 0.0f;
    		for (i = 0; i < 4; i++)
    		{
    			det += (i & 0x1) ? (-in_m[i] * DetIJ(in_m, 0, i)) : (in_m[i] * DetIJ(in_m, 0,i));
    		}
     
    		det = 1.0f / det;
     
    		// calculate inverse
    		for (i = 0; i < 4; i++)
    		{
    			for (j = 0; j < 4; j++)
    			{
    				detij = DetIJ(in_m, j, i);
    				inverse[(i * 4) + j] = ((i + j) & 0x1) ? (-detij * det) : (detij * det); 
    			}
    		}
    	}
     
    void MathGL::TransformPointByMatrix(Vector3 *result, float *mat, Vector3 *point)
    	{
    		result->x = (mat[0] * point->x) + (mat[4] * point->y) + (mat[8] * point->z) + mat[12];
    		result->y = (mat[1] * point->x) + (mat[5] * point->y) + (mat[9] * point->z) + mat[13];
    		result->z = (mat[2] * point->x) + (mat[6] * point->y) + (mat[10] * point->z) + mat[14];
     
    		return;
    	}

    //Detection
    Code :
    	//parametric ray test
    	bool Detect::BoxIntersectRay(Vector3 *min, Vector3 *max, Vector3 *point, Vector3 *dir)
    	{
    		static bool inside;
    		static int which;
    		static float t;
    		static float xt, yt, zt;
    		static float xn, yn, zn;
    		static float x, y, z;
     
    		inside = true;
     
    		if (point->x < min->x)
    		{
    			xt = min->x - point->x;
    			if (xt > dir->x)
    				return false;
    			xt /= dir->x;
    			inside = false;
    			xn = -1.0f;
    		}
    		else if (point->x > max->x)
    		{
    			xt = max->x - point->x;
    			if (xt > dir->x)
    				return false;
    			xt /= dir->x;
    			inside = false;
    			xn = 1.0f;
    		}
    		else
    		{
    			xt = -1.0f;
    		}
     
    		if (point->y < min->y)
    		{
    			yt = min->y - point->y;
    			if (yt > dir->y)
    				return false;
    			yt /= dir->y;
    			inside = false;
    			yn = -1.0f;
    		}
    		else if (point->y > max->y)
    		{
    			yt = max->y - point->y;
    			if (yt > dir->y)
    				return false;
    			yt /= dir->y;
    			inside = false;
    			yn = 1.0f;
    		}
    		else
    		{
    			yt = -1.0f;
    		}
     
    		if (point->z < min->z)
    		{
    			zt = min->z - point->z;
    			if (zt > dir->z)
    				return false;
    			zt /= dir->z;
    			inside = false;
    			zn = -1.0f;
    		}
    		else if (point->z > max->z)
    		{
    			zt = max->z - point->z;
    			if (zt > dir->z)
    				return false;
    			zt /= dir->z;
    			inside = false;
    			zn = 1.0f;
    		}
    		else
    		{
    			zt = -1.0f;
    		}
     
    		if (inside == true)
    		{
    			return true;
    		}
     
    		//Select farthest plane, this is the
    		//plane of intersection
     
    		which = 0;
    		t = xt;
     
    		if (yt > t)
    		{
    			which = 1;
    			t = yt;
    		}
     
    		if (zt > t)
    		{
    			which = 2;
    			t = zt;
    		}
     
    		switch (which)
    		{
    		case 0: //intersect with yz plane
    			y = point->y + dir->y * t;
    			if (y < min->y || (y > max->y))
    				return false;
     
    			z = point->z + dir->z * t;
    			if (z < min->z || (z > max->z))
    				return false;
    		break;
     
    		case 1: //intersect with xz plane
    			x = point->x + dir->x * t;
    			if (x < min->x || (x > max->x))
    				return false;
     
    			z = point->z + dir->z * t;
    			if (z < min->z || (z > max->z))
    				return false;
    		break;
     
    		case 2: //intersect with xy plane
    			x = point->x + dir->x * t;
    			if (x < min->x || (x > max->x))
    				return false;
     
    			y = point->y + dir->y * t;
    			if (y < min->y || (y > max->y))
    				return false;
    		break;
     
    		default:
    			return false;
    		}
     
    		//if reach here then intersection
    		return true;
    	}
     
    	/*bool Detect::BoxIntersectRay(Vector3 *bpos, Vector3 *bsize, Vector3 *point, Vector3 *dir)
    	{
    		static float maxS;
    		static float minT;
     
    		maxS = -FLT_MAX;//could be 0.0f
    		minT = FLT_MAX;
     
    		// do tests against three sets of planes
     
    		//X plane
    		// ray is parallel to plane
    		if (dir->x == 0.0f)
    		{
    			// ray passes by box
    			if ((point->x < bpos->x) || (point->x > bsize->x))
    				return false;
    		}
    		else
    		{
    			static float s, t, temp;
     
    			// compute intersection parameters and sort
    			s = (bpos->x - point->x / dir->x);
    			t = (bsize->x - point->x / dir->x);
    			if (s > t)
    			{
    				temp = s;
    				s = t;
    				t = temp;
    			}
     
    			// adjust min and max values
    			if (s > maxS)
    				maxS = s;
    			if (t < minT)
    				minT = t;
    			// check for intersection failure
    			if ((minT < 0.0f) || (maxS > minT))
    				return false;
    		}
     
    		//Y plane
    		// ray is parallel to plane
    		if (dir->y == 0.0f)
    		{
    			// ray passes by box
    			if ((point->y < bpos->y) || (point->y > bsize->y))
    				return false;
    		}
    		else
    		{
    			static float s, t, temp;
     
    			// compute intersection parameters and sort
    			s = (bpos->y - point->y / dir->y);
    			t = (bsize->y - point->y / dir->y);
    			if (s > t)
    			{
    				temp = s;
    				s = t;
    				t = temp;
    			}
     
    			// adjust min and max values
    			if (s > maxS)
    				maxS = s;
    			if (t < minT)
    				minT = t;
    			// check for intersection failure
    			if ((minT < 0.0f) || (maxS > minT))
    				return false;
    		}
     
    		//Z plane
    		// ray is parallel to plane
    		if (dir->z == 0.0f)
    		{
    			// ray passes by box
    			if ((point->z < bpos->z) || (point->z > bsize->z))
    				return false;
    		}
    		else
    		{
    			static float s, t, temp;
     
    			// compute intersection parameters and sort
    			s = (bpos->z - point->z / dir->z);
    			t = (bsize->z - point->z / dir->z);
    			if (s > t)
    			{
    				temp = s;
    				s = t;
    				t = temp;
    			}
     
    			// adjust min and max values
    			if (s > maxS)
    				maxS = s;
    			if (t < minT)
    				minT = t;
    			// check for intersection failure
    			if ((minT < 0.0f) || (maxS > minT))
    				return false;
    		}
     
    		// done, have intersection
    		return true;
    	}*/

    //Initialization of map
    Code :
    	void Map::CreateTiles()
    	{
    	    if (m_tiles_loaded == true)
    	    {
    	        e_engine->Error("Called CreateTiles without first destroying the tiles");
    	    }
     
            m_tile = new sfuTile[m_length];
    		//m_tile_coords = new Vector3[m_length];
    		m_bbox = new sfuMapTilesBoundingBoxes[m_length];
            m_wide_tile = new sfuWideTile[m_wide_length];
     
    		memset(m_tile, 0, sizeof(sfuTile) * m_length);
    		memset(m_wide_tile, 0, sizeof(sfuWideTile) * m_wide_length);
     
    		e_camera->setMapScrollingBoundies(0.0f, 0.0f, -10.0f, (m_width * m_tile_width), (m_height * m_tile_height), 10.0f);
     
    		Vector3 pos;
    		Vector3 size;
    		Vector3 direction;
    		float tc[4];
     
    		memset(&pos, 0, sizeof(Vector3));
    		memset(&direction, 0, sizeof(Vector3));
     
    		pos.z = -2.5f;
     
    		size.x = m_tile_width;
    		size.y = m_tile_height;
    		size.z = 0.0f;
     
    		tc[0] = 0.0f;
    		tc[1] = 0.0f;
    		tc[2] = 1.0f;
    		tc[3] = 1.0f;
     
    		int x, y;
    		//work out the tiles
    		for (y = 0; y < m_height; y++)
    		{
    			for (x = 0; x < m_width; x++)
    			{
    				m_tile[x + (y * m_width)].o_sb = i_sb->AddBillboard(&pos, &size, &direction, NULL, tc, FOXY_OPT_TEXS | FOXY_OPT_DIRS);
     
    				//bounding box for picking
    				memcpy(&(m_bbox[x + (y * m_width)].min), &pos, sizeof(Vector3));
     
    				m_bbox[x + (y * m_width)].max.x = pos.x + m_tile_width;
    				m_bbox[x + (y * m_width)].max.y = pos.y + m_tile_height;
    				m_bbox[x + (y * m_width)].max.z = pos.z + 0.3f; //m_tile_zth_step;
     
    ///TODO: need a workout depth tile and a entire map func aswell, also add the m_bbox and get rid of m_tile_coords
    ///this is more for loading in or changing the depth value - would need to re-work these out
    ///the above however is perfect for creation
    				//swap z values
    				/*if (m_bbox[x + (y * m_width)].min.z > m_bbox[x + (y * m_width)].max.z)
    				{
    					temp = m_bbox[x + (y * m_width)].min.z;
    					m_bbox[x + (y * m_width)].min.z = m_bbox[x + (y * m_width)].max.z;
    					m_bbox[x + (y * m_width)].min.z = temp;
    				}*/
    //remember
    //ensure we dont overstep the bounds of the map, need to figure that out later (should draw one less aswell but perhaps add one more above to solve the problem)
     
    				pos.x += m_tile_width;
    			}
     
    			pos.x -= m_width;
    			pos.y += m_tile_height;
    		}		
    	}

    Anyway i think ive got it all there.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •