How to use Qt3DRender::QObjectPicker with touch events?
I'm adding Qt3DRender::QObjectPicker component to my Qt3D entities by this method:
Qt3DRender::QObjectPicker *MyClass::createObjectPickerForEntity(Qt3DCore::QEntity *entity)
{
Qt3DRender::QObjectPicker *picker = new Qt3DRender::QObjectPicker(entity);
picker->setHoverEnabled(false);
entity->addComponent(picker);
connect(picker, &Qt3DRender::QObjectPicker::pressed, this, &MyClass::handlePickerPress);
return picker;
}
My object picker works with mouse clicks, but it doesn't work with touch events. Does anybody know how I can use Qt3D object picker with touch events on smartphones?
#FlorianBlume helped me to solve the problem. Touch on Qt3D entities can be detected with QScreenRayCaster. I had to add a QScreenRayCaster component to my root entity:
/*
* You have to add the ray caster to the root entity as a component
* Perform ray casting tests by specifying "touch" coordinates in screen space
*/
m_screenRayCaster = new Qt3DRender::QScreenRayCaster(m_rootEntity);
m_screenRayCaster->setRunMode(Qt3DRender::QAbstractRayCaster::SingleShot);
m_rootEntity->addComponent(m_screenRayCaster);
/*
* Handle ray casting results by signal-slot connection
* "QScreenRayCaster::hitsChanged" signal contains ray casting result for any hit
* "MyClass::handleScreenRayCasterHits" slot needs to be implemented to handle hit results
*/
QObject::connect(m_screenRayCaster, &Qt3DRender::QScreenRayCaster::hitsChanged, this, &MyClass::handleScreenRayCasterHits);
I trigger QScreenRayCaster tests by touch events like this using m_screenRayCaster->trigger() method:
void MyClass::HandleTouchEvent(QTouchEvent *event)
{
switch (event->type()) {
case QEvent::TouchBegin:
break;
case QEvent::TouchEnd:
if (event->touchPoints().count() == 1) {
QPointF point = event->touchPoints().at(0).pos();
m_screenRayCaster->trigger(QPoint(static_cast<int>(point.x()), static_cast<int>(point.y())));
}
break;
default:
break;
}
}
Handling ray casting results in MyClass::handleScreenRayCasterHits slot:
void MyClass::handleScreenRayCasterHits(const Qt3DRender::QAbstractRayCaster::Hits hits)
{
for (int i = 0; i < hits.length(); ++i) {
qDebug() << __func__ << "Hit Type: " << hits.at(i).type();
qDebug() << __func__ << "Hit entity name: " << hits.at(i).entity()->objectName();
}
}
Related
Okay so I'm making a photography game where when you 'take a photo', Unity sends a few raycasts forward to check if certain tagged items are in the photo (all within the cameras FOV). My problem is, this seems to work intermittently! Sometimes it finds the tagged objects, other times it will be right in front of the view yet it will miss it completely! Can anyone advise about what I'm doing wrong?
public static Transform target;
public static GameObject[] targetName;
public static float length = 250f;
public static Transform thisObject;
// Start is called before the first frame update
void Start()
{
thisObject = GameObject.Find("Main Camera").GetComponent<Transform>();
//target = GameObject.FindGameObjectWithTag("Trees").transform;
}
// Update is called once per frame
void Update()
{
//InFront();
//HasLineOfSight("Trees");
}
public static bool InFront(Transform target1)
{
Vector3 directionToTarget = thisObject.position - target1.position;
float angleOnXAxis = Vector3.Angle(thisObject.right, directionToTarget);
float angleOnYAxis = Vector3.Angle(thisObject.up, directionToTarget);
//Debug.Log(angleOnYAxis);
if (Mathf.Abs(angleOnXAxis) < 130 && Mathf.Abs(angleOnXAxis) > 50
&& Mathf.Abs(angleOnYAxis) < 115 && Mathf.Abs(angleOnYAxis) > 62)
{
//Debug.DrawLine(transform.position, target.position, Color.green);
return true;
}
return false;
}
public static bool HasLineOfSight(string objectTag)
{
RaycastHit hit;
Vector3 direction = target.position - thisObject.position;
//Debug.Log(direction);
if (Physics.Raycast(thisObject.position, direction, out hit, length))
{
if (hit.transform.tag == objectTag)
{
Debug.DrawRay(thisObject.position, direction * 0.96f, Color.red);
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
public static GameObject SortObjects(string objectTag)
{
targetName = GameObject.FindGameObjectsWithTag(objectTag);
GameObject closestObject = null;
for (int i = 0; i < targetName.Length; i++)
{
if (Vector3.Distance(thisObject.position,
targetName[i].transform.position) <= length)
{
if (InFront(targetName[i].transform))
{
if (closestObject == null)
{
closestObject = targetName[i];
}
else
{
if (Vector3.Distance(targetName[i].transform.position, thisObject.position) <= Vector3.Distance(closestObject.transform.position, thisObject.position))
{
closestObject = targetName[i];
}
}
}
}
}
return closestObject;
}
public static bool ObjectCheck(string objectTag)
{
//Debug.Log(SortObjects(objectTag));
if (SortObjects(objectTag) != null)
{
target = SortObjects(objectTag).transform;
//Debug.Log(target);
if (InFront(target) && HasLineOfSight(objectTag))
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
I'm essentially calling ObjectCheck() with the tag I want to check for to get the closest, visible, object with that tag. What is wrong with this code??
In your script, only the closest object to the main camera gets checked. SortObjects() determines the closest tagged object, and then you only handle that single object in ObjectCheck(). - That object might be obstructed by something else, so the method returns false. And other tagged objects that are actually visible, are not picked up this way...
So, you could rename and change your SortObjects() function to check for both conditions right in the loop (InFront(target) && HasLineOfSight(objectTag)), and filter the objects out right in there, since only those objects are of interest.
Also, your HasLineOfSight() method checks the tag of the hit object, but what you probably wanted to do, is to check if the raycast actually hits that exact object. So it should instead compare the hit's gameObject to the target's gameObject, ignoring the tag, since a correct tag alone isn't enough. (Side note: it would make sense to place all "photographable objects" on a "photo layer", and set the layer mask in the Physics.Raycast() call accordingly, it's more efficient that way in larger scenes.)
The way the angles are calculated in the InFront() method is probably causing issues, because the direction vector to the target is really in 3D. To calculate the angles, you could try to use Vector3.Project() or Vector3.ProjectOnPlane(), but that will also be problematic, because of perspective camera issues.
This check is strongly related to the topic of "frustum culling", a technique usually used for rendering. But it's similar to what you need, to filter out all the (possibly) visible objects in the camera's field of view (frustum culling doesn't handle obstruction, it is just a geometric check to see if a point lies within the camera's frustum space). See:
https://en.wikipedia.org/wiki/Viewing_frustum
https://en.wikipedia.org/wiki/Hidden-surface_determination#Viewing-
http://www.lighthouse3d.com/tutorials/view-frustum-culling/
https://docs.unity3d.com/Manual/UnderstandingFrustum.html
If you want to dig deeper and optimize this, there are a couple of ways this can be done. But luckily, Unity comes with many useful related functions already built into the Camera class. So instead, you could use Camera.WorldToScreenPoint() (or Camera.WorldToViewportPoint()), and compare the resulting screen coordinates to the screen size or viewport, like discussed in Unity forum. (The frustum math is hidden behind these compact functions, but beware that this is probably not the optimal way to do this.)
Instead of calling FindGameObjectsWithTag() every time, you could do it only once in Start(), assuming objects do not get created/destroyed while the game is running.
I've tried to modify your script, since I'm also learning Unity again... The script can be dragged to the main camera, and it should show the "focus object" in the Scene view with the green debug line. I hope this helps:
using UnityEngine;
[RequireComponent(typeof(Camera))]
public class PhotoCast : MonoBehaviour
{
public float maxDistance = 250.0f;
public string objectTag = "photo";
protected GameObject[] objs;
protected GameObject objFocus;
protected Camera cam;
public void Start() {
objs = GameObject.FindGameObjectsWithTag(objectTag);
cam = GetComponent<Camera>();
}
public void Update() {
if (Input.GetButtonDown("Fire1")) {
objFocus = CheckObjects();
if (objFocus) {
Debug.Log("closest object in view: " + objFocus.name);
/* TODO: take actual photo here */
}
}
if (objFocus) {
Debug.DrawLine(transform.position,
objFocus.transform.position, Color.green);
}
}
GameObject CheckObjects() {
GameObject obj_closest = null;
float dist_closest = float.MaxValue;
foreach (GameObject o in objs) {
float dist = Vector3.Distance(
o.transform.position, transform.position);
if (dist < maxDistance && dist < dist_closest
&& InViewport(o.transform.position)
&& HasLineOfSight(o.transform)) {
dist_closest = dist;
obj_closest = o;
}
}
return obj_closest;
}
bool InViewport(Vector3 worldPos) {
Vector3 p = cam.WorldToViewportPoint(worldPos);
return (p.x > 0.0f && p.x <= 1.0f && p.y > 0.0f && p.y <= 1.0f
&& p.z > cam.nearClipPlane);
}
bool HasLineOfSight(Transform target) {
RaycastHit hit;
Vector3 dir = target.position - transform.position;
if (Physics.Raycast(transform.position, dir, out hit, maxDistance)) {
if (hit.collider.gameObject == target.gameObject) {
return true;
}
}
return false;
}
}
Side notes:
Another issue with this technique is, that there can be tagged objects right in front of the camera, but other tagged objects that are closer on the side will be picked up instead of the obvious one. Many small issues to fine-tune until the scripts fits the game, I guess. Instead of only using one Raycast per object, you could use multiple ones, and take the bounding box or the actual collider shape into account.
An improved version of the script could make use of the Physics.Overlap*() or Physics.*Cast*() functions, documented here.
I am a masters student working on localization, using ranging (time of arrival between vehicle and RSU) and relative location (Using emulated Inertial Navigation System).
I have done an implementation of my kalman filter based localization logic on Matlab, now I would like to implement this on veins. I want only the RSU to send out a message comprising of its location and ID
1) I know that i can use
double Coord = mobility->getCurrentPosition().x;
double Coord = mobility->getCurrentPosition().y;
to the location of RSU(and my vehicle as well), I do not understand how I should assign these coordinates to the message. I cannot use sstream since I understand that the message are supposed to be of type const char *
Thanks for any input
Edit 1: So this is what my new code on RSU looks like:
#include "RsuScOne.h"
#include <sstream>
Define_Module(RsuScOne);
void RsuScOne::initialize(int stage) {
BaseWaveApplLayer::initialize(stage);
if (stage == 0) {
//Initializing members and pointers of your application goes here
//WaveShortMessage* wsm = new WaveShortMessage();
EV << "Initializing " << std::endl;
}
else if (stage == 1) {
//Initializing members that require initialized other modules goes here
}
}
void RsuScOne::finish() {
BaseWaveApplLayer::finish();
//statistics recording goes here
cancelEvent(sendWSAEvt);
}
void RsuScOne::onWSM(WaveShortMessage* wsm) {
//Your application has received a data message from another car or RSU
//code for handling the message goes here, see TraciDemo11p.cc for examples
populateWSM(wsm);
std::stringstream ss;
ss<<mobility->getCurrentPosition().x<<mobility->getCurrentPosition().y;
wsm->setWsmData(ss.str().c_str());
scheduleAt(simTime()+par("beaconInterval").doubleValue(), sendWSAEvt);
EV<<wsm;
}
void RsuScOne::handleSelfMsg(cMessage* msg) {
BaseWaveApplLayer::handleSelfMsg(msg);
}
But I realize that all that being done now is my RSU constantly sending a generic BSM, Why is this so?
I've been trying to figure this out all day. I'm trying to make a simple check box. But my MOUSEBUTTONUP event keeps firing until I trigger another event like moving the mouse or clicking again.
//If a mouse button was released
if (Sdl_Setup->GetMainEvent()->type == SDL_MOUSEBUTTONUP)
{
//If the left mouse button was pressed
if (Sdl_Setup->GetMainEvent()->button.button == SDL_BUTTON_LEFT)
{
//Get the mouse offsets
SDL_GetMouseState(&mouseX, &mouseY);
//If the mouse is over the button
if ((mouseX > Button->GetX()) && (mouseX < Button->GetX() + Button->GetWidth()) && (mouseY > Button->GetY()) && (mouseY < Button->GetY() + Button->GetHeight()))
{
//Set the button sprite
state = selected;
Button->SetCrop(GetFrameX(), GetFrameY(), state);
clicked = true;
std::cout << clicked << std::endl;
}
}
}
This will just spam 1 in the console (if you dont move the mouse) when all I want is for it to trigger once. From what I've read MOUSEBUTTONUP is only supposed to be sent to the event queue once
I've tried adding a bool to stop it after it's clicked by putting it in an if statement and that works but the thing is I want to be able to toggle the box on and off so when I add an else statement that changes the bool back to false it spams 101010 instead of just one.
//If a mouse button was released
if (Sdl_Setup->GetMainEvent()->type == SDL_MOUSEBUTTONUP)
{
//If the left mouse button was pressed
if (Sdl_Setup->GetMainEvent()->button.button == SDL_BUTTON_LEFT)
{
//Get the mouse offsets
SDL_GetMouseState(&mouseX, &mouseY);
//If the mouse is over the button
if ((mouseX > Button->GetX()) && (mouseX < Button->GetX() + Button->GetWidth()) && (mouseY > Button->GetY()) && (mouseY < Button->GetY() + Button->GetHeight()))
{
if (clicked == false)
{
//Set the button sprite
state = selected;
Button->SetCrop(GetFrameX(), GetFrameY(), state);
clicked = true;
std::cout << clicked << std::endl;
}
else
{
//Set the button sprite
state = noInteraction;
Button->SetCrop(GetFrameX(), GetFrameY(), state);
clicked = false;
std::cout << clicked << std::endl;
}
}
}
}
In Sdl_Setup is where I make the window I'm using and stuff like that and the variable mainEvent is = new SDL_Event(); so then when I was calling GetMainEvent() it was just sending that to PollEvent() over and over again. So all I did is create a quick void function in Sdl_Setup to set mainEvent to a blank SDL_Event() and then call that after clicked = true; and it works.
I have searched the web a lot but couldn't find exactly what I want!
suppose that I have a class derived from CWnd. In fact, it is the class COpenGLControl here in codeguru customized by me for my own purposes.
the event handler for the WM_MOUSEMOVE button is written as follows:
void COpenGLControl::OnMouseMove(UINT nFlags, CPoint point)
{
int diffX = (int)(point.x - m_fLastX);
int diffY = (int)(point.y - m_fLastY);
m_fLastX = (float)point.x;
m_fLastY = (float)point.y;
// Left mouse button
if (nFlags & MK_LBUTTON)
{
m_fRotX += (float)0.5f * diffY;
if ((m_fRotX > 360.0f) || (m_fRotX < -360.0f))
{
m_fRotX = 0.0f;
}
m_fRotY += (float)0.5f * diffX;
if ((m_fRotY > 360.0f) || (m_fRotY < -360.0f))
{
m_fRotY = 0.0f;
}
}
// Right mouse button
else if (nFlags & MK_RBUTTON)
{
m_fZoom -= (float)0.1f * diffY;
}
// Middle mouse button
else if (nFlags & MK_MBUTTON)
{
m_fPosX += (float)0.05f * diffX;
m_fPosY -= (float)0.05f * diffY;
}
OnDraw(NULL);
CWnd::OnMouseMove(nFlags, point);
}
But I don't want this event handler to be active or enabled at all times. I want to put three buttons on my dialog named pan,rotate and zoom.
when I click pan I want OnMouseMove get active just for middle button
when I click rotate I want middle button get inactive and left button get active
when I click zoom I want left button get inactive and right one get active and so.
and finally when I click another button like Zoom extent, select and etc, I want the OnMouseMove event handler get inactive in such a way that even if I'm on the opengl window the Maya-style mouse won't be active?
How can I implement something like this whether in my customized COpenGLControl class or in My MFC dialog?
Please give some instructions to me to begin my search to find out more.
------------------------------------------------------------------------------------------ Edited part of my question
I also thought about adding an event-handler manually to my class just like the OnDraw function in the COpenGLControl class so did something like this:
OpenGLContro.h
afx_msg void Pan(UINT nFlags, CPoint point);
OpenGLControl.cpp
void COpenGLControl::Pan(UINT nFlags, CPoint point)
{
int diffX = (int)(point.x - m_fLastX);
int diffY = (int)(point.y - m_fLastY);
if (nFlags & MK_MBUTTON)
{
m_fPosX += (float)0.05f * diffX;
m_fPosY -= (float)0.05f * diffY;
}
OnDraw(NULL);
}
and whenever the button pan is clicked I will call this function but there I'm not still on the OpenGL Window and don't know hat to pass as the parameters to the function Pan?
Add a state/mode member variable to the class and for each "mode" a dedicated handler function. The event handlers you use the mode variable to decide, which of the mode dependent handlers you call from the event handler, passing all the parameters.
Is there a way to limit mouse pointer movement to a specific area in wxWidgets? I know there is an API function ClipCursor() in Windows, but is there a method in wxWidgets for all platforms?
No. There is no such function in wx by all i know. Start up a timer (say 50ms) checking the global mouse position. If the mouse is outside the region, then set it into again.
If you want to restrict the mouse for some certain reason, for example to make some sort of game, then you can capture the mouse (see wxWindow::CaptureMouse). You will get mouse events even if the pointer is outside your window. Then you could react to mouse-motion events and do the check for the position there, without a timer. Downside of this is that the mouse won't be able to be used somewhere else for other programs since they won't receive events.
wxWidgets manual states that OSX guidelines forbid the programs to set the mouse pointer to a certain position programmatically. That might contribute to the reason there is not much support for such stuff in wx, especially since wx tries really hard to be compatible to everything possible.
Small sample. Click on the button to restrict the mouse to area 0,0,100,100. Click somewhere to release it.
#include <wx/wx.h>
namespace sample {
class MyWin : public wxFrame {
public:
MyWin()
:wxFrame(0, wxID_ANY, wxT("haha title")) {
mRestricted = wxRect(0, 0, 100, 100);
mLast = mRestricted.GetTopLeft();
wxButton * button = new wxButton(this, wxID_ANY, wxT("click this"));
}
private:
void OnClicked(wxCommandEvent& event) {
if(!HasCapture()) {
CaptureMouse();
CheckPosition();
}
}
void OnMotion(wxMouseEvent& event) {
CheckPosition();
}
void OnLeft(wxMouseEvent& event) {
if(HasCapture())
ReleaseMouse();
}
void CheckPosition() {
wxPoint pos = wxGetMousePosition();
if(!mRestricted.Contains(pos)) {
pos = ScreenToClient(mLast);
WarpPointer(pos.x, pos.y);
} else {
mLast = pos;
}
}
wxRect mRestricted;
wxPoint mLast;
DECLARE_EVENT_TABLE();
};
BEGIN_EVENT_TABLE(MyWin, wxFrame)
EVT_BUTTON(wxID_ANY, MyWin::OnClicked)
EVT_MOTION(MyWin::OnMotion)
EVT_LEFT_DOWN(MyWin::OnLeft)
END_EVENT_TABLE()
class MyApp : public wxApp {
virtual bool OnInit() {
MyWin * win = new MyWin;
win -> Show();
SetTopWindow(win);
return true;
}
};
} /* sample:: */
IMPLEMENT_APP(sample::MyApp)