📄 cvhittst.cpp
字号:
/*----------------------------------------------------------------------------
_ _ _
/\ | | | (_)
/ \ _ __ __| |_ __ ___ _ __ ___ ___ __| |_ __ _
/ /\ \ | '_ \ / _` | '__/ _ \| '_ ` _ \ / _ \/ _` | |/ _` |
/ ____ \| | | | (_| | | | (_) | | | | | | __/ (_| | | (_| |
/_/ \_\_| |_|\__,_|_| \___/|_| |_| |_|\___|\__,_|_|\__,_|
The contents of this file are subject to the Andromedia Public
License Version 1.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of
the License at http://www.andromedia.com/APL/
Software distributed under the License is distributed on an
"AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
implied. See the License for the specific language governing
rights and limitations under the License.
The Original Code is Pueblo client code, released November 4, 1998.
The Initial Developer of the Original Code is Andromedia Incorporated.
Portions created by Andromedia are Copyright (C) 1998 Andromedia
Incorporated. All Rights Reserved.
Andromedia Incorporated 415.365.6700
818 Mission Street - 2nd Floor 415.365.6701 fax
San Francisco, CA 94103
Contributor(s):
--------------------------------------------------------------------------
Chaco team: Dan Greening, Glenn Crocker, Jim Doubek,
Coyote Lussier, Pritham Shetty.
Wrote and designed original codebase.
------------------------------------------------------------------------------
Implementation for the computation of bounding boxes of a VRML QV tree
(currently assumes Intel 3DR.)
----------------------------------------------------------------------------*/
// $Header: /home/cvs/chaco/modules/client/msw/ChGraphx/CvHitTst.cpp,v 2.20 1996/06/27 03:44:00 jimd Exp $
#include "grheader.h"
//
#if defined( CH_ARCH_16 )
#include <QvElemnt.h>
#else
#include <QvElement.h>
#endif
//#include <QvNodes.h>
#include <QvState.h>
#if defined( CH_ARCH_16 )
#include <UnknNode.h>
#else
#include <QvUnknownNode.h>
#endif
#ifdef CH_MSW
#include <windows.h>
#endif
#ifdef HUGE
#undef HUGE
#endif
#include <math.h>
#include "ChMaze.h"
#include "CvBound.h"
#include "CvHitTst.h"
#include "CvTrnsfm.h"
// Even if we render purely retained, we need this if we use RL
#if (defined(CH_USE_RLAB) || defined(CH_USE_D3D))
#include "imode.h"
#endif
#define DEFAULT_HITTEST(cls) \
int cls::HitTest(QvState *state, ChQvBounds * pBounds) \
{traverse(state); return 0;};
// for dubugging only
static int indent = 0;
// ---------------
// maybe make this a method of GxVec3f??
// PointDistToLine :
// Returns the minimum distance between the line (p0, v) and the point q
// Optionally, return the t for the min point, where
// p = p0 + v * t
float PointDistToLine(const GxVec3f &p0, const GxVec3f &v, const GxVec3f &q, float *pT)
{
// We do it by minimizing squared distance from line p = p0 + t * v to
// the point q.
//
GxVec3f alpha(p0);
alpha -= q;
float t = -(alpha.x() * v.x() + alpha.y() * v.y() + alpha.z() * v.z()) / v.dot(v);
if(pT) *pT = t;
GxVec3f p(v);
p *= t;
p += p0; // p is now nearest point
p -= q; // now diff with q
return p.magnitude();
};
// Intersect a sphere of radius r1 moving along the ray (p0, v) with the sphere
// of radius r2 centered at q
// Return true if they intersect, and place the t intersection value in fT
// fT is unchanged unless intersection happens
bool MovingSphereToSphere(const GxVec3f &p0, const GxVec3f &v, float r1, const GxVec3f &q, float r2, float &fT)
{
// We do it by minimizing squared distance from line p = p0 + t * v to
// the point q.
//
GxVec3f alpha(p0);
alpha -= q;
float a = v.dot(v);
float b = 2 * (alpha.x() * v.x() + alpha.y() * v.y() + alpha.z() * v.z());
float r = r1 + r2;
float c = alpha.dot(alpha) - r * r;
float desc = b * b - 4. * a * c;
if(desc < 0) return false;
float rt = sqrt(desc);
float t1 = -(b - rt)/ (2. * a);
float t2 = -(b + rt)/ (2. * a);
// Select the minimum positive value of t1 and t2
bool boolHit = false;
if(t1 >= 0 && t2 >= 0)
{
fT = min(t1, t2);
boolHit = true;
}
else if(t1 >= 0)
{
fT = t1;
boolHit = true;
}
else if(t2 >= 0)
{
fT = t2;
boolHit = true;
}
// else all intersections are behind us in negative t
return boolHit;
};
void QvWWWAnchor::UpdateSensor(QvState * state)
{
#if defined(CH_USE_3DR)
G3dHandle_t hGC = ((ChQvState*)state)->GetView()->GetGC();
G3dSetActiveStack(hGC, G3DT_CAM_CLIP);
G3dPushTransform(hGC);
G3dSetActiveStack(hGC, G3DT_MODEL);
G3dPushTransform(hGC);
G3dSetActiveStack(hGC, G3DT_CAM_CLIP);
G3dPopTransform(hGC);
G3dSetActiveStack(hGC, G3DT_MODEL);
G3dPopTransform(hGC);
#endif
}
#if defined(CH_USE_3DR)
void ChQvAnchorSensor::Init(ChRenderContext *pRC)
{
G3dHandle_t hGC = pRC->GetGC();
PointF_t dir, up;
G3dGetCameraPosition(hGC, &m_cameraLoc, &dir, &up);
G3dSetActiveStack(hGC, G3DT_CAM_CLIP);
G3dGetTransform(hGC, m_CamClipTransform);
G3dSetActiveStack(hGC, G3DT_MODEL);
float right, left, top, bottom;
G3dGetCameraPort(hGC, &right, &left, &top, &bottom);
SetPort(left, top, right, bottom);
// hack: TODO - FIX THIS
// put the instance's transform on the stack - oughta be ignored later, we hope
pRC->SetModelTransform(m_pAnchor->GetTransform());
memcpy(m_ModelTransform, G3dGetModelCamMatrix(hGC), sizeof(m_ModelTransform));
}
#endif
#if defined(CH_USE_3DR)
inline void GetCubeVertex( PointF_t &lower, PointF_t &upper, int num, PointF_t &vert)
{
vert.x = (num & 4) ? upper.x : lower.x;
vert.y = (num & 2) ? upper.y : lower.y;
vert.z = (num & 1) ? upper.z : lower.z;
}
inline PointF_t GetCubeVertex( PointF_t &lower, PointF_t &upper, int num)
{ PointF_t vert;
vert.x = (num & 4) ? upper.x : lower.x;
vert.y = (num & 2) ? upper.y : lower.y;
vert.z = (num & 1) ? upper.z : lower.z;
return vert;
}
IntersectLineCube(LineF_t &line, PointF_t &lower, PointF_t &upper, PointF_t &ptInt, TransformF_t mat, bool boolRay = true);
#if !defined(EPSILON)
#define EPSILON (1.e-6)
#endif
IntersectLineCube(LineF_t &line, PointF_t &lower, PointF_t &upper, PointF_t &ptInt, TransformF_t mat, bool boolRay)
{
float fZNear;
bool boolHit = false;
// these are ccw, tho it doesn't matter here
static int verts[6][4] =
{
{0, 2, 6, 4}, // back
{1, 5, 7, 3}, // front
{0, 1, 3, 2}, // lhs
{4, 6, 7, 5}, // rhs
{0, 4, 1, 5}, // bottom
{2, 3, 7, 6} // top
};
for(int j = 0; j < 6; j++)
{
PlaneF_t plane;
PointF_t pt;
PointF_t vert0, vert1, vert2;
GetCubeVertex(lower, upper, verts[j][0], vert0);
GetCubeVertex(lower, upper, verts[j][1], vert1);
GetCubeVertex(lower, upper, verts[j][2], vert2);
G3d3PointPlane(&vert0, &vert1, &vert2, &plane);
if(G3dLinePlaneIntersect(&line, &plane, &pt))
{
// Make the cube bigger to account for FP roundoff
bool boolIntersect =
pt.x >= lower.x - EPSILON && pt.x <= upper.x + EPSILON &&
pt.y >= lower.y - EPSILON && pt.y <= upper.y + EPSILON &&
pt.z >= lower.z - EPSILON && pt.z <= upper.z + EPSILON;
// Check for 'behind' the line origin, if treating this as a ray
if(boolRay)
{
PointF_t v1;
v1.x = pt.x - line.point.x;
v1.y = pt.y - line.point.y;
v1.z = pt.z - line.point.z;
if(G3dDot(&v1, &line.vector) < 0) boolIntersect = false;
}
if(boolIntersect)
{
// transform to camera space to check z
// looking for -nearest- intersection
PointFW_t pt2;
G3dTransformPointF(&pt, &pt2, mat);
if(boolHit)
{ // neg z is farther ==> greater is nearer
if(pt2.z > fZNear)
{
// new min; replace old intersection
ptInt = pt;
fZNear = pt2.z;
}
}
else
{
// first face to intersect with this ray
ptInt = pt;
fZNear = pt2.z;
boolHit = true;
}
}
}
}
return boolHit;
}
// Computes the line in object space that goes thru wx, wy in window space
void ChQvAnchorSensor::PointWindowToObject(Float_t wx, Float_t wy, LineF_t & line)
{
PointF_t pt = {wx, wy, 0};
PointF_t pt2;
PointFW_t ptW;
if(m_boolDirty)
{
G3dInvertMatrix(m_CamClipTransform, m_invCamClipTransform);
G3dInvertMatrix(m_ModelTransform, m_invModelTransform);
m_boolDirty = false;
}
pt.x = ((pt.x * 2.) / (m_right - m_left)) - 1.;
pt.y = ((pt.y * 2.) / (m_top - m_bottom)) - 1.;
pt.y = -pt.y;
G3dTransformPointF(&pt, &ptW, m_invCamClipTransform);
CopyPoint(ptW, pt2);
G3dTransformPointF(&pt2, &ptW, m_invModelTransform);
CopyPoint(ptW, line.vector);
/////////////
// Camera is in world coords; convert to model
GxTransform3Wf mat;
TransformF_t invMat;
GxVec3f lower;
GxVec3f upper;
m_bounds.GetBounds( upper, lower, mat);
CopyPoint(m_cameraLoc, pt2);
G3dInvertMatrix(*(mat.GetMatrix()), invMat);
G3dTransformPointF(&pt2, &ptW, invMat);
CopyPoint(ptW, line.point);
///////////////
line.vector.x -= line.point.x;
line.vector.y -= line.point.y;
line.vector.z -= line.point.z;
}
bool ChQvAnchorSensor::HitTest(G3dHandle_t hGC, chint32 lX, chint32 lY, float &fZ)
{
GxVec3f lower;
GxVec3f upper;
PointF_t pt;
PointFW_t pt2;
GxTransform3Wf mat;
bool boolHit = false;
m_bounds.GetBounds( lower, upper, mat);
LineF_t line;
PointWindowToObject(lX, lY, line);
// This function doesn't work - reported to Intel : case #46594
//G3dPointWindowToObject(hGC, xx, yy, &line);
//G3dPointWindowToObject(hGC, lX, lY, &line);
// this gets nearest pt in WS; i think it should be camera
// space instead ??!! which means the mat should be the MCT
//if(IntersectLineCube(line, lower, upper, pt, mat))
// Like this
TransformF_t toClipMat;
G3dMultMatrix( m_ModelTransform, m_CamClipTransform, toClipMat);
//if(IntersectLineCube(line, lower, upper, pt, toClipMat))
PointF_t lowerp, upperp;
lowerp.x = lower.x(); lowerp.y = lower.y(); lowerp.z = lower.z();
upperp.x = upper.x(); upperp.y = upper.y(); upperp.z = upper.z();
if(IntersectLineCube(line, lowerp, upperp, pt, m_ModelTransform))
{
boolHit = true;
// transform pt, then
G3dTransformPointF(&pt, &pt2, m_ModelTransform);
fZ = pt2.z;
}
return boolHit;
}
#endif
string ChQvAnchorSensor::GetCommand()
{
#if !defined(CH_VRML_VIEWER) && !defined(CH_VRML_PLUGIN )
QvInfo * pChild;
string command, phrase, name, href;
for (int i = 0; i < GetAnchorNode()->getNumChildren(); i++)
{
pChild = (QvInfo*)(GetAnchorNode()->getChild(i));
// Test if this is really a info node
string type;
pChild->GetType(type);
if(type == "QvInfo")
{
name = pChild->getName().getString();
string prefix = name.Left(3);
prefix.MakeLower();
if(prefix == "xch")
{
phrase = name;
string value = pChild->string.value.getString();
if(value != QV_UNDEFINED_INFO)
{
phrase += "=\"" + value + "\"";
}
command += phrase + " ";
}
}
}
name = GetAnchorNode()->name.value.getString();
if(!name.IsEmpty() && name != QV_UNDEFINED_INFO)
{
href.Format("href=\"%s\"", name);
command += href;
}
name = command;
#else
// Scout
string name;
name = GetAnchorNode()->name.value.getString();
#endif
// Append the point field, if we have it
#if (defined(CH_USE_RLAB) || defined(CH_USE_D3D))
if(!name.IsEmpty() && m_pAnchor->IsPointMap())
{
string buf;
buf.Format("?%1.2f,%1.2f,%1.2f", double(m_hitPoint.x()),double(m_hitPoint.y()),double(m_hitPoint.z()));
name += buf;
}
#else
// hit point isn't being converted yet!
#endif
return name;
}
bool ChQvAnchorSensor::GetHint(string &hint)
{
QvInfo * pChild;
string name;
bool boolFound = false;
string cmd;
// if there's a description, use that, else look at the info kids
name = GetAnchorNode()->description.value.getString();
if(!name.IsEmpty())
{
hint = name;
boolFound = true;
// we could cache this if it's too slow
for (int i = 0; i < GetAnchorNode()->getNumChildren(); i++)
{
pChild = (QvInfo*)(GetAnchorNode()->getChild(i));
// Test if this is really a info node
string type;
pChild->GetType(type);
if(type == "QvInfo")
{
name = pChild->getName().getString();
name.MakeLower();
if(name == "xch_cmd")
{
cmd = pChild->string.value.getString();
}
}
}
}
else
{
// we could cache this if it's too slow
for (int i = 0; i < GetAnchorNode()->getNumChildren(); i++)
{
pChild = (QvInfo*)(GetAnchorNode()->getChild(i));
// Test if this is really a info node
string type;
pChild->GetType(type);
if(type == "QvInfo")
{
name = pChild->getName().getString();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -