mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-05 13:17:49 +00:00
153 lines
5.7 KiB
C++
153 lines
5.7 KiB
C++
#include "GfxBase/ViewportBillboarder.h"
|
|
#include "V8DataModel/Filters.h"
|
|
#include "V8DataModel/Camera.h"
|
|
#include "V8World/World.h"
|
|
#include "V8World/ContactManager.h"
|
|
|
|
namespace RBX {
|
|
|
|
ViewportBillboarder::ViewportBillboarder()
|
|
:guiScreenSize(NULL)
|
|
,alwaysOnTop(false)
|
|
,screenOffset2D(Vector2::zero())
|
|
{}
|
|
ViewportBillboarder::ViewportBillboarder(
|
|
const Vector3& partExtentRelativeOffset,
|
|
const Vector3& partStudsOffset,
|
|
const Vector2& billboardSizeRelativeOffset,
|
|
const UDim2& billboardSize,
|
|
const Vector2* guiScreenSize)
|
|
:partExtentRelativeOffset(partExtentRelativeOffset)
|
|
,partStudsOffset(partStudsOffset)
|
|
,billboardSizeRelativeOffset(billboardSizeRelativeOffset)
|
|
,billboardSize(billboardSize)
|
|
,guiScreenSize(guiScreenSize)
|
|
,alwaysOnTop(false)
|
|
,screenOffset2D(Vector2::zero())
|
|
{}
|
|
|
|
Vector2 ViewportBillboarder::getScreenOffset(const Rect2D& parentviewport, const RBX::Camera& camera, const CoordinateFrame& desiredModelView)
|
|
{
|
|
const CoordinateFrame& cameraFrame = camera.coordinateFrame();
|
|
|
|
Vector3 projectedTranslation = camera.project((cameraFrame * desiredModelView).translation);
|
|
|
|
return Math::roundVector2(projectedTranslation.xy());
|
|
}
|
|
|
|
void ViewportBillboarder::update(const Rect2D& parentviewport, const Camera& camera, Vector3 partSize, CoordinateFrame partCFrame)
|
|
{
|
|
Vector3 halfSize = partSize/2;
|
|
|
|
Extents cameraSpaceExtents;
|
|
|
|
// calculate extents in camera space
|
|
for(int a = 0; a < 8; a++)
|
|
{
|
|
// go through all permutations (use bits of counter)
|
|
Vector3 extent((a & 4) ? halfSize.x : -halfSize.x,
|
|
(a & 2) ? halfSize.y : -halfSize.y,
|
|
(a & 1) ? halfSize.z : -halfSize.z);
|
|
Vector3 pextent = camera.coordinateFrame().pointToObjectSpace(partCFrame.pointToWorldSpace(extent));
|
|
cameraSpaceExtents.expandToContain(pextent);
|
|
}
|
|
|
|
Vector3 relativeOffsetInCameraSpace = (partExtentRelativeOffset + Vector3::one()) * 0.5f * cameraSpaceExtents.size() + cameraSpaceExtents.min();
|
|
Vector3 billboardCenterInCameraSpace = relativeOffsetInCameraSpace + partStudsOffset;
|
|
|
|
Vector3 billboardCenterInWorldSpace = camera.coordinateFrame().pointToWorldSpace(billboardCenterInCameraSpace);
|
|
Vector3 billboardCenterInProjSpace = camera.project(billboardCenterInWorldSpace);
|
|
|
|
if(billboardCenterInProjSpace.z > 0 && billboardCenterInProjSpace.z < 1000)
|
|
{
|
|
visibleAndValid = true;
|
|
}
|
|
else
|
|
{
|
|
visibleAndValid = false;
|
|
return;
|
|
}
|
|
|
|
float pixelsPerStud = billboardCenterInProjSpace.z;
|
|
Vector2 studsPerPixel(1.f/ pixelsPerStud, 1.f/ pixelsPerStud); // undo the perspective effect of finding extents in screenspace.
|
|
// -1 : to UI coordinates (0,0 upper left, )
|
|
Vector2 billboardSizeInStuds = (billboardSize * (Vector2::one()*pixelsPerStud)) * studsPerPixel;
|
|
if(guiScreenSize)
|
|
viewport = Rect2D(*guiScreenSize);
|
|
else
|
|
viewport = Rect2D(billboardSizeInStuds * pixelsPerStud);
|
|
|
|
Vector2 UIToCameraSpaceScaler(billboardSizeInStuds/ viewport.wh());
|
|
|
|
billboardCenterInCameraSpace += Vector3(billboardSizeRelativeOffset * billboardSizeInStuds, 0);
|
|
|
|
CoordinateFrame desiredModelView;
|
|
desiredModelView.translation = billboardCenterInCameraSpace - Vector3(billboardSizeInStuds.x * 0.5f, billboardSizeInStuds.y * -0.5f, 0);
|
|
desiredModelView.rotation.set(UIToCameraSpaceScaler.x, 0,0,0,UIToCameraSpaceScaler.y, 0,0,0, 1);
|
|
|
|
if (alwaysOnTop)
|
|
screenOffset2D = getScreenOffset(parentviewport, camera, desiredModelView);
|
|
|
|
cframe = camera.coordinateFrame() * desiredModelView;
|
|
}
|
|
|
|
bool ViewportBillboarder::hitTest(const Vector2int16& mousePosition, const Vector2int16& windowSize,
|
|
RBX::Workspace* workspace,
|
|
Vector2& billboardMousePosition)
|
|
{
|
|
const RBX::Camera& camera = *(workspace->getConstCamera());
|
|
|
|
Vector3 x0y0Screen, x1y1Screen;
|
|
|
|
if (alwaysOnTop)
|
|
{
|
|
x0y0Screen = Vector3(screenOffset2D, 0);
|
|
x1y1Screen = Vector3(screenOffset2D + viewport.wh(), 0);
|
|
}
|
|
else
|
|
{
|
|
Vector3 x0y0World = cframe.pointToWorldSpace(Vector3(viewport.x0(), -viewport.y0(),0));
|
|
Vector3 x1y1World = cframe.pointToWorldSpace(Vector3(viewport.x1(), -viewport.y1(),0));
|
|
|
|
x0y0Screen = camera.project(x0y0World);
|
|
x1y1Screen = camera.project(x1y1World);
|
|
}
|
|
|
|
Extents extents;
|
|
extents.expandToContain(Vector3(x0y0Screen.xy(),0));
|
|
extents.expandToContain(Vector3(x1y1Screen.xy(),0));
|
|
|
|
if(extents.contains(Vector3(mousePosition.x, mousePosition.y, 0)))
|
|
{
|
|
billboardMousePosition = Vector2((mousePosition.x - x0y0Screen.x)/(x1y1Screen.x - x0y0Screen.x)*viewport.width(), (mousePosition.y - x0y0Screen.y)/(x1y1Screen.y - x0y0Screen.y)*viewport.height());
|
|
|
|
if (alwaysOnTop) {
|
|
return true;
|
|
}
|
|
ContactManager& contactManager = *workspace->getWorld()->getContactManager();
|
|
RbxRay unitRay = camera.worldRay(mousePosition.x, mousePosition.y);
|
|
RbxRay searchRay = RbxRay::fromOriginAndDirection(unitRay.origin(), unitRay.direction() * 2048);
|
|
Vector3 partHitPointWorld;
|
|
FilterInvisibleNonColliding invisibleNonCollidingObjectsFilter; // invisible & non-colliding parts don't block mouse-clicks
|
|
if(contactManager.getHit( searchRay, NULL, &invisibleNonCollidingObjectsFilter, partHitPointWorld) == NULL)
|
|
{
|
|
//We didn't hit anything
|
|
return true;
|
|
}
|
|
|
|
Vector3 hitPointWorld = cframe.pointToWorldSpace(Vector3(billboardMousePosition.x, billboardMousePosition.y, 0));
|
|
Vector3 hitPointScreen = camera.project(hitPointWorld);
|
|
|
|
Vector3 partHitPointScreen = camera.project(partHitPointWorld);
|
|
if(partHitPointScreen.z < hitPointScreen.z)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
}
|