Skip to content

Commit

Permalink
UPBGE: Fix orthographic camera getScreenRay.
Browse files Browse the repository at this point in the history
Previously the function getScreenRay was adapted to only perspective
cameras because the ray cast was always starting from the center of
the camera.
In case of orthographic the ray cast should start from a point unprojected
from the near plane and continue to in the direction of the camera
(-Z orientation axis).
This is proceeded in getScreenRay and the indirect call to getScreenVect
is removed.

Fix issue #765.
  • Loading branch information
panzergame committed Aug 30, 2018
1 parent 6e8870b commit 229ff4e
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 51 deletions.
78 changes: 44 additions & 34 deletions source/gameengine/Ketsji/KX_Camera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "KX_Scene.h"
#include "KX_Globals.h"
#include "KX_PyMath.h"
#include "KX_RayCast.h"

#include "RAS_ICanvas.h"

Expand Down Expand Up @@ -883,24 +884,21 @@ EXP_PYMETHODDEF_DOC_VARARGS(KX_Camera, getScreenVect,
"getScreenVect()\n"
)
{
double x, y;
if (!PyArg_ParseTuple(args, "dd:getScreenVect", &x, &y)) {
float x, y;
if (!PyArg_ParseTuple(args, "ff:getScreenVect", &x, &y)) {
return nullptr;
}

y = 1.0 - y; //to follow Blender window coordinate system (Top-Down)

const mt::mat4 modelmatrix = mt::mat4::FromAffineTransform(GetWorldToCamera());
const mt::mat4& projmatrix = this->GetProjectionMatrix();

RAS_ICanvas *canvas = KX_GetActiveEngine()->GetCanvas();
const int width = canvas->GetWidth();
const int height = canvas->GetHeight();

const mt::vec3 vect(x *width, y *height, 0.0f);

const mt::vec3 screenpos = mt::mat4::UnProject(vect, modelmatrix, projmatrix, width, height);

const mt::vec3 vect(x * width, y * height, 0.0f);
const mt::vec3 screenpos = mt::mat4::UnProject(vect, modelmatrix, m_projection_matrix, width, height);
const mt::vec3 ret = (NodeGetLocalPosition() - screenpos).Normalized();

return PyObjectFrom(ret);
Expand All @@ -910,44 +908,56 @@ EXP_PYMETHODDEF_DOC_VARARGS(KX_Camera, getScreenRay,
"getScreenRay()\n"
)
{
mt::vec3 vect;
double x, y, dist;
float x, y, dist;
char *propName = nullptr;

if (!PyArg_ParseTuple(args, "ddd|s:getScreenRay", &x, &y, &dist, &propName)) {
if (!PyArg_ParseTuple(args, "fff|s:getScreenRay", &x, &y, &dist, &propName)) {
return nullptr;
}

PyObject *argValue = PyTuple_New(2);
PyTuple_SET_ITEM(argValue, 0, PyFloat_FromDouble(x));
PyTuple_SET_ITEM(argValue, 1, PyFloat_FromDouble(y));
y = 1.0 - y; //to follow Blender window coordinate system (Top-Down)

if (!PyVecTo(PygetScreenVect(argValue), vect)) {
Py_DECREF(argValue);
PyErr_SetString(PyExc_TypeError,
"Error in getScreenRay. Invalid 2D coordinate. "
"Expected a normalized 2D screen coordinate, "
"a distance and an optional property argument");
return nullptr;
const mt::mat4 modelmatrix = mt::mat4::FromAffineTransform(GetWorldToCamera());

RAS_ICanvas *canvas = KX_GetActiveEngine()->GetCanvas();
const int width = canvas->GetWidth();
const int height = canvas->GetHeight();

mt::vec3 fromPoint;
mt::vec3 toPoint;

// Unproject a point in near plane.
const mt::vec3 point(x * width, y * height, 0.0f);
const mt::vec3 screenpos = mt::mat4::UnProject(point, modelmatrix, m_projection_matrix, width, height);

// For perpspective the vector is from camera center to unprojected point.
if (m_camdata.m_perspective) {
fromPoint = NodeGetWorldPosition();
toPoint = screenpos;
}
// For orthographic the vector is the same as the -Z rotation axis but start from unprojected point.
else {
fromPoint = screenpos;
toPoint = fromPoint - NodeGetWorldOrientation().GetColumn(2);
}
Py_DECREF(argValue);

dist = -dist;
vect += NodeGetWorldPosition();
if (dist != 0.0f) {
toPoint = fromPoint + dist * (toPoint - fromPoint).SafeNormalized(mt::axisX3);
}

argValue = (propName ? PyTuple_New(3) : PyTuple_New(2));
if (argValue) {
PyTuple_SET_ITEM(argValue, 0, PyObjectFrom(vect));
PyTuple_SET_ITEM(argValue, 1, PyFloat_FromDouble(dist));
if (propName) {
PyTuple_SET_ITEM(argValue, 2, PyUnicode_FromString(propName));
}
PHY_IPhysicsEnvironment *pe = GetScene()->GetPhysicsEnvironment();
PHY_IPhysicsController *spc = m_physicsController.get();
KX_GameObject *parent = GetParent();
if (!spc && parent) {
spc = parent->GetPhysicsController();
}

PyObject *ret = this->PyrayCastTo(argValue, nullptr);
Py_DECREF(argValue);
return ret;
RayCastData rayData("", false, (1u << OB_MAX_COL_MASKS) - 1);
KX_RayCast::Callback<KX_Camera, RayCastData> callback(this, spc, &rayData);
if (KX_RayCast::RayTest(pe, fromPoint, toPoint, callback) && rayData.m_hitObject) {
return rayData.m_hitObject->GetProxy();
}

return nullptr;
Py_RETURN_NONE;
}
#endif
25 changes: 9 additions & 16 deletions source/gameengine/Ketsji/KX_GameObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3886,20 +3886,13 @@ EXP_PYMETHODDEF_DOC_O(KX_GameObject, getVectTo,
return returnValue;
}

struct KX_GameObject::RayCastData {
RayCastData(std::string prop, bool xray, unsigned int mask)
:m_prop(prop),
m_xray(xray),
m_mask(mask),
m_hitObject(nullptr)
{
}

std::string m_prop;
bool m_xray;
unsigned int m_mask;
KX_GameObject *m_hitObject;
};
KX_GameObject::RayCastData::RayCastData(const std::string& prop, bool xray, unsigned int mask)
:m_prop(prop),
m_xray(xray),
m_mask(mask),
m_hitObject(nullptr)
{
}

static bool CheckRayCastObject(KX_GameObject *obj, KX_GameObject::RayCastData *rayData)
{
Expand Down Expand Up @@ -4104,14 +4097,14 @@ EXP_PYMETHODDEF_DOC(KX_GameObject, rayCast,

if (dist != 0.0f) {
mt::vec3 toDir = toPoint - fromPoint;
if (mt::FuzzyZero(toDir.LengthSquared())) {
if (mt::FuzzyZero(toDir)) {
//return Py_BuildValue("OOO", Py_None, Py_None, Py_None);
return none_tuple_3();
}
toDir.Normalize();
toPoint = fromPoint + (dist) * toDir;
}
else if (mt::FuzzyZero((toPoint - fromPoint).LengthSquared())) {
else if (mt::FuzzyZero(toPoint - fromPoint)) {
//return Py_BuildValue("OOO", Py_None, Py_None, Py_None);
return none_tuple_3();
}
Expand Down
10 changes: 9 additions & 1 deletion source/gameengine/Ketsji/KX_GameObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,15 @@ class KX_GameObject : public SCA_IObject, public mt::SimdClassAllocator
* This structure is created during ray cast and passed as argument
* "data" to functions KX_GameObject::NeedRayCast and KX_GameObject::RayHit.
*/
struct RayCastData;
struct RayCastData
{
RayCastData(const std::string& prop, bool xray, unsigned int mask);

std::string m_prop;
bool m_xray;
unsigned int m_mask;
KX_GameObject *m_hitObject;
};

/**
* Helper function for modules that can't include KX_ClientObjectInfo.h
Expand Down

0 comments on commit 229ff4e

Please sign in to comment.