mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
1300 lines
42 KiB
C++
1300 lines
42 KiB
C++
/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */
|
|
|
|
#include "AppDraw/DrawAdorn.h"
|
|
#include "AppDraw/DrawPrimitives.h"
|
|
#include "GfxBase/Part.h"
|
|
#include "GfxBase/Adorn.h"
|
|
#include "AppDraw/HitTest.h"
|
|
#include "Util/Math.h"
|
|
#include "Util/Rect.h"
|
|
#include "Util/Extents.h"
|
|
#include "Util/HitTest.h"
|
|
#include "Util/Face.h"
|
|
#include "GfxBase/MeshGen.h"
|
|
#include "V8DataModel/Camera.h"
|
|
#include "V8DataModel/PartInstance.h"
|
|
#include "G3D/Color3uint8.h"
|
|
|
|
FASTFLAGVARIABLE(Studio3DGridUseAALines, true)
|
|
|
|
namespace RBX {
|
|
|
|
static const float ZEROPLANE_GRID_SIZE_BASE = 400.0f;
|
|
static const float ZEROPLANE_GRID_FACTOR = 8.0f;
|
|
static const float HandleOffset = 0.5f;
|
|
static const float RotationGridLineThickness = 0.045f;
|
|
static const float RotationGridLineMinAngle = Math::degreesToRadians(2.5f);
|
|
static const float TorusThickness = 0.025f;
|
|
static const float TorusThicknessMinAngle = Math::degreesToRadians(2.5f);
|
|
static const float SphereRadius = 0.5f;
|
|
static const float SphereMinAngle = Math::degreesToRadians(2.0f);
|
|
static const float ArrowLength = 3.0f;
|
|
static const float ArrowMinAngle = Math::degreesToRadians(3.5f);
|
|
static const float HandleTransparency = 0.65f;
|
|
static const float AxisTransparency = 0.28f;
|
|
|
|
const Color3 DrawAdorn::axisColors[3] =
|
|
{
|
|
Color3::red(),
|
|
Color3::green(),
|
|
Color3::blue()
|
|
};
|
|
|
|
// common colors
|
|
const Color3 DrawAdorn::beige(G3D::Color3uint8(0xF5,0xF5,0xDC));
|
|
const Color3 DrawAdorn::darkblue(G3D::Color3uint8(0x00,0x00,0x8B));
|
|
const Color3 DrawAdorn::powderblue(G3D::Color3uint8(0xB0,0xE0,0xE6));
|
|
const Color3 DrawAdorn::skyblue(G3D::Color3uint8(0x87,0xCE,0xEB));
|
|
const Color3 DrawAdorn::violet(G3D::Color3uint8(0xEE,0x82,0xEE));
|
|
const Color3 DrawAdorn::slategray(G3D::Color3uint8(0x70,0x80,0x90));
|
|
const Color3 DrawAdorn::aqua(G3D::Color3uint8(0x00,0xFF,0xFF));
|
|
const Color3 DrawAdorn::tan(G3D::Color3uint8(0xD2,0xB4,0x8C));
|
|
const Color3 DrawAdorn::wheat(G3D::Color3uint8(0xF5,0xDE,0xB3));
|
|
const Color3 DrawAdorn::cornflowerblue(G3D::Color3uint8(0x64,0x95,0xED));
|
|
const Color3 DrawAdorn::limegreen(G3D::Color3uint8(0x32,0xCD,0x32));
|
|
const Color3 DrawAdorn::magenta(G3D::Color3uint8(0xFF,0x00,0xFF));
|
|
const Color3 DrawAdorn::pink(G3D::Color3uint8(0xFF,0xC0,0xCB));
|
|
const Color3 DrawAdorn::silver(G3D::Color3uint8(0xC0,0xC0,0xC0));
|
|
|
|
void DrawAdorn::cylinder(
|
|
Adorn* adorn,
|
|
const CoordinateFrame& worldC,
|
|
int axis,
|
|
float length,
|
|
float radius,
|
|
const Color4& color,
|
|
bool cap)
|
|
{
|
|
const Matrix3& relativeRotation = Math::getAxisRotationMatrix(axis);
|
|
CoordinateFrame rotatedWorldC(worldC.rotation * relativeRotation, worldC.translation);
|
|
|
|
adorn->setObjectToWorldMatrix(rotatedWorldC);
|
|
adorn->cylinderAlongX(radius, length, color, cap);
|
|
}
|
|
|
|
|
|
|
|
void DrawAdorn::faceInWorld(Adorn* adorn,
|
|
const Face& face,
|
|
float thickness,
|
|
const Color4& color)
|
|
{
|
|
Vector3 x = (face[1] - face[0]);
|
|
Vector3 y = (face[3] - face[0]);
|
|
Vector3 z = x.cross(y);
|
|
Vector3 center = (face[0] + face[2]) * 0.5;
|
|
|
|
CoordinateFrame c(center);
|
|
|
|
c.rotation.setColumn(0, x.unit());
|
|
c.rotation.setColumn(1, y.unit());
|
|
c.rotation.setColumn(2, z.unit());
|
|
|
|
adorn->setObjectToWorldMatrix(c);
|
|
|
|
float dx = x.magnitude() * 0.5f;
|
|
float dy = y.magnitude() * 0.5f;
|
|
|
|
|
|
Extents extents( Vector3(-dx, -dy, -thickness), Vector3(dx, dy, thickness) );
|
|
adorn->box(extents, color);
|
|
}
|
|
|
|
|
|
void DrawAdorn::surfaceBorder(Adorn* adorn,
|
|
const Vector3& halfRealSize,
|
|
float highlight,
|
|
int surfaceId,
|
|
const Color4& color)
|
|
{
|
|
int c1 = surfaceId % 3; // x, y, or z - primary
|
|
float cZ = halfRealSize[c1] * ((surfaceId < 3) ? 1 : -1);
|
|
Vector3 p0, p1;
|
|
p0[c1] = cZ - highlight;
|
|
p1[c1] = cZ + highlight;
|
|
|
|
int c2 = (c1 + 1) % 3; // y
|
|
int c3 = (c1 + 2) % 3; // z
|
|
for (int direction = 0; direction < 2; ++direction) {
|
|
int cY = direction ? c2 : c3;
|
|
int cZ = direction ? c3 : c2;
|
|
for (int polarity = -1; polarity <= 1; polarity += 2) {
|
|
p0[cY] = polarity * halfRealSize[cY] - highlight;
|
|
p1[cY] = polarity * halfRealSize[cY] + highlight;
|
|
p0[cZ] = -halfRealSize[cZ] - highlight;
|
|
p1[cZ] = halfRealSize[cZ] + highlight;
|
|
|
|
AABox(p0, p1);
|
|
|
|
adorn->box(AABox(p0, p1), color);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DrawAdorn::surfaceGridOnFace(
|
|
const Primitive& prim,
|
|
Adorn* adorn,
|
|
int surfaceId,
|
|
const Color4& color,
|
|
int boxesPerStud )
|
|
{
|
|
CoordinateFrame bodyCoord = prim.getCoordinateFrame() ;
|
|
CoordinateFrame faceCoord = bodyCoord * prim.getConstGeometry()->getSurfaceCoordInBody(surfaceId);
|
|
|
|
Vector4 bounds(-1e10f, -1e10f, 1e10f, 1e10f);
|
|
for( int i = 0; i < prim.getConstGeometry()->getNumVertsInSurface(surfaceId); i++ )
|
|
{
|
|
Vector3 vertInBody = prim.getConstGeometry()->getSurfaceVertInBody(surfaceId, i);
|
|
Vector3 vertInWorld = bodyCoord.pointToWorldSpace(vertInBody);
|
|
Vector3 vertInFace = faceCoord.pointToObjectSpace(vertInWorld);
|
|
|
|
// max vector
|
|
if( vertInFace.x > bounds.x )
|
|
bounds.x = vertInFace.x;
|
|
if( vertInFace.y > bounds.y )
|
|
bounds.y = vertInFace.y;
|
|
|
|
// Min vector
|
|
if( vertInFace.x < bounds.z )
|
|
bounds.z = vertInFace.x;
|
|
if( vertInFace.y < bounds.w )
|
|
bounds.w = vertInFace.y;
|
|
}
|
|
surfaceGridAtCoord(adorn, faceCoord, bounds, Vector3::unitX(), Vector3::unitY(), color, boxesPerStud);
|
|
}
|
|
|
|
|
|
void DrawAdorn::zeroPlaneGrid(
|
|
Adorn* adorn,
|
|
const RBX::Camera& camera,
|
|
const int studsPerBox,
|
|
const int yLevel,
|
|
const G3D::Color4& smallGridColor,
|
|
const G3D::Color4& largeGridColor)
|
|
{
|
|
const Vector3 cameraPos = camera.getCameraCoordinateFrame().translation;
|
|
const Vector3 zeroPlaneCenter = G3D::Plane(Vector3(0,1,0),Vector3(0,yLevel,0)).closestPoint(cameraPos);
|
|
const int x = (int) (Math::iRound((zeroPlaneCenter.x + studsPerBox/2.0f)/studsPerBox) * studsPerBox);
|
|
const int z = (int) (Math::iRound((zeroPlaneCenter.z + studsPerBox/2.0f)/studsPerBox) * studsPerBox);
|
|
const Vector3 zeroPlaneCenterOnGrid = Vector3(x,zeroPlaneCenter.y,z);
|
|
const float distFromPlane = (cameraPos - zeroPlaneCenter).magnitude();
|
|
const float lineThickness = 2;
|
|
|
|
const int largeGridStep = studsPerBox * ZEROPLANE_GRID_FACTOR;
|
|
const int lineSegmentStep = largeGridStep;
|
|
|
|
adorn->setObjectToWorldMatrix(Vector3::zero());
|
|
|
|
// if we are close enough to the zero plane, lets render the smaller grid
|
|
if(distFromPlane < ZEROPLANE_GRID_SIZE_BASE)
|
|
{
|
|
const float zEnd = ZEROPLANE_GRID_SIZE_BASE + zeroPlaneCenter.z;
|
|
const float zStart = -ZEROPLANE_GRID_SIZE_BASE + zeroPlaneCenter.z;
|
|
const float xEnd = ZEROPLANE_GRID_SIZE_BASE + zeroPlaneCenter.x;
|
|
const float xStart = -ZEROPLANE_GRID_SIZE_BASE + zeroPlaneCenter.x;
|
|
|
|
const int zeroPlaneSmallCenterX = Math::iRound(zeroPlaneCenterOnGrid.x/studsPerBox) * studsPerBox;
|
|
const int zeroPlaneSmallCenterZ = Math::iRound(zeroPlaneCenterOnGrid.z/studsPerBox) * studsPerBox;
|
|
|
|
for(int i = -ZEROPLANE_GRID_SIZE_BASE + zeroPlaneSmallCenterX; i < (ZEROPLANE_GRID_SIZE_BASE + zeroPlaneSmallCenterX); i += studsPerBox )
|
|
{
|
|
float lastStep = zStart;
|
|
for(int j = zStart + lineSegmentStep; j <= zEnd + lineSegmentStep; j += lineSegmentStep)
|
|
{
|
|
const float cameraToPlaneDistance = (Vector3(i,zeroPlaneCenterOnGrid.y,j + (j - lastStep)/2.0f) - cameraPos).magnitude();
|
|
const float alpha = std::max(1.0f - (cameraToPlaneDistance/ZEROPLANE_GRID_SIZE_BASE),0.0f);
|
|
if(alpha > 0.0f)
|
|
{
|
|
if (FFlag::Studio3DGridUseAALines)
|
|
adorn->line3dAA(Vector3(i,zeroPlaneCenterOnGrid.y,lastStep),
|
|
Vector3(i,zeroPlaneCenterOnGrid.y,j),
|
|
RBX::Color4(smallGridColor.rgb(),alpha),
|
|
lineThickness, 0, false);
|
|
else
|
|
adorn->line3d(Vector3(i,zeroPlaneCenterOnGrid.y,lastStep),
|
|
Vector3(i,zeroPlaneCenterOnGrid.y,j),
|
|
RBX::Color4(smallGridColor.rgb(),alpha));
|
|
}
|
|
lastStep = j;
|
|
}
|
|
}
|
|
for(int i = -ZEROPLANE_GRID_SIZE_BASE + zeroPlaneSmallCenterZ; i < (ZEROPLANE_GRID_SIZE_BASE + zeroPlaneSmallCenterZ); i += studsPerBox )
|
|
{
|
|
float lastStep = xStart;
|
|
for(int j = xStart + lineSegmentStep; j <= xEnd + lineSegmentStep; j += lineSegmentStep)
|
|
{
|
|
const float cameraToPlaneDistance = (Vector3(j + (j - lastStep)/2.0f,zeroPlaneCenterOnGrid.y,i) - cameraPos).magnitude();
|
|
const float alpha = std::max(1.0f - (cameraToPlaneDistance/ZEROPLANE_GRID_SIZE_BASE),0.0f);
|
|
if(alpha > 0.0f)
|
|
{
|
|
if (FFlag::Studio3DGridUseAALines)
|
|
adorn->line3dAA(Vector3(lastStep,zeroPlaneCenterOnGrid.y,i),
|
|
Vector3(j,zeroPlaneCenterOnGrid.y,i),
|
|
RBX::Color4(smallGridColor.rgb(),alpha),
|
|
lineThickness, 0, false);
|
|
else
|
|
adorn->line3d(Vector3(lastStep,zeroPlaneCenterOnGrid.y,i),
|
|
Vector3(j,zeroPlaneCenterOnGrid.y,i),
|
|
RBX::Color4(smallGridColor.rgb(),alpha));
|
|
}
|
|
lastStep = j;
|
|
}
|
|
}
|
|
}
|
|
|
|
// now render the large grid
|
|
const int zeroPlaneLargeCenterX = Math::iRound(zeroPlaneCenterOnGrid.x/largeGridStep) * largeGridStep;
|
|
const int zeroPlaneLargeCenterZ = Math::iRound(zeroPlaneCenterOnGrid.z/largeGridStep) * largeGridStep;
|
|
|
|
for(int i = -ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterX; i <= (ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterX); i += largeGridStep )
|
|
{
|
|
const Vector3 lineStart = Vector3(i,zeroPlaneCenterOnGrid.y,-ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterZ);
|
|
const Vector3 lineEnd = Vector3(i,zeroPlaneCenterOnGrid.y,ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterZ);
|
|
const Vector3 midPoint = lineEnd + ((lineEnd - lineStart) * 0.5f);
|
|
const float distFromPlane = (cameraPos - midPoint).magnitude();
|
|
|
|
const float largeGridAlpha = std::max(1.0f - ((distFromPlane * 1.5f)/(ZEROPLANE_GRID_SIZE_BASE)),0.25f);
|
|
if (FFlag::Studio3DGridUseAALines)
|
|
adorn->line3dAA(lineStart,
|
|
lineEnd,
|
|
RBX::Color4(largeGridColor.rgb(),largeGridAlpha),
|
|
lineThickness, 0, false);
|
|
else
|
|
adorn->line3d(lineStart,
|
|
lineEnd,
|
|
RBX::Color4(largeGridColor.rgb(),largeGridAlpha));
|
|
}
|
|
for(int i = -ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterZ; i <= (ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterZ); i += largeGridStep )
|
|
{
|
|
const Vector3 lineStart = Vector3(i,zeroPlaneCenterOnGrid.y,-ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterZ);
|
|
const Vector3 lineEnd = Vector3(i,zeroPlaneCenterOnGrid.y,ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterZ);
|
|
const Vector3 midPoint = lineEnd + ((lineEnd - lineStart) * 0.5f);
|
|
const float distFromPlane = (cameraPos - midPoint).magnitude();
|
|
|
|
const float largeGridAlpha = std::max(1.0f - ((distFromPlane * 1.5f)/(ZEROPLANE_GRID_SIZE_BASE)),0.25f);
|
|
if (FFlag::Studio3DGridUseAALines)
|
|
adorn->line3dAA(Vector3(-ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterX,zeroPlaneCenterOnGrid.y,i),
|
|
Vector3(ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterX,zeroPlaneCenterOnGrid.y,i),
|
|
RBX::Color4(largeGridColor.rgb(),largeGridAlpha),
|
|
lineThickness, 0, false);
|
|
else
|
|
adorn->line3d(Vector3(-ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterX,zeroPlaneCenterOnGrid.y,i),
|
|
Vector3(ZEROPLANE_GRID_SIZE_BASE + zeroPlaneLargeCenterX,zeroPlaneCenterOnGrid.y,i),
|
|
RBX::Color4(largeGridColor.rgb(),largeGridAlpha));
|
|
}
|
|
|
|
// last, we render the axes rays at the origin
|
|
static const float ArrowSize = 4.0f;
|
|
if ( camera.frustum().intersectsSphere(Vector3::zero(),ArrowSize) )
|
|
{
|
|
adorn->setMaterial(Adorn::Material_SelfLitHighlight);
|
|
adorn->setObjectToWorldMatrix(CoordinateFrame(Vector3(0,0,0)));
|
|
adorn->ray(RBX::RbxRay(Vector3(0,0,0),Vector3(ArrowSize,0,0)),axisColors[0]);
|
|
adorn->ray(RBX::RbxRay(Vector3(0,0,0),Vector3(0,ArrowSize,0)),axisColors[1]);
|
|
adorn->ray(RBX::RbxRay(Vector3(0,0,0),Vector3(0,0,ArrowSize)),axisColors[2]);
|
|
adorn->setMaterial(Adorn::Material_Default);
|
|
}
|
|
}
|
|
|
|
void DrawAdorn::surfaceGridAtCoord(
|
|
Adorn* adorn,
|
|
CoordinateFrame& cF,
|
|
const Vector4& bounds,
|
|
const Vector3& gridXDir,
|
|
const Vector3& gridYDir,
|
|
const G3D::Color4& color,
|
|
int boxesPerStud )
|
|
{
|
|
int direction1 = 1;
|
|
int direction2 = 0;
|
|
int xMaxInt = Math::iRound(bounds.x + 1);
|
|
int yMaxInt = Math::iRound(bounds.y + 1);
|
|
int xMinInt = Math::iRound(bounds.z - 1);
|
|
int yMinInt = Math::iRound(bounds.w - 1);
|
|
|
|
Matrix3 planeCoordRot = Math::fromDirectionCosines(gridXDir, gridYDir, gridXDir.cross(gridYDir), Vector3::unitX(), Vector3::unitY(), Vector3::unitZ());
|
|
|
|
CoordinateFrame planeCoord = cF;
|
|
planeCoord.rotation *= planeCoordRot;
|
|
CoordinateFrame adornCoord = planeCoord;
|
|
|
|
Vector3 ptXDir = Vector3::zero();
|
|
ptXDir.y = 0.5f * (yMaxInt + yMinInt);
|
|
for( int i = xMinInt; i <= xMaxInt; i++ )
|
|
{
|
|
if( boxesPerStud == 0 && i > xMinInt )
|
|
i = xMaxInt;
|
|
ptXDir.x = (float)i;
|
|
Vector3 ptInWorld = planeCoord.pointToWorldSpace(ptXDir);
|
|
adornCoord.translation = ptInWorld;
|
|
cylinder(adorn, adornCoord, direction1, (float)(yMaxInt - yMinInt), 0.03, color);
|
|
if( i < xMaxInt )
|
|
{
|
|
for( int j = 1; j < boxesPerStud; j++ )
|
|
{
|
|
ptXDir.x = (float)i + (1.0f/(float)boxesPerStud) * (float)j;
|
|
ptInWorld = planeCoord.pointToWorldSpace(ptXDir);
|
|
adornCoord.translation = ptInWorld;
|
|
cylinder(adorn, adornCoord, direction1, (float)(yMaxInt - yMinInt), 0.01, color);
|
|
}
|
|
}
|
|
}
|
|
|
|
Vector3 ptYDir = Vector3::zero();
|
|
ptYDir.x = 0.5f * (xMaxInt + xMinInt);
|
|
for( int i = yMinInt; i <= yMaxInt; i++ )
|
|
{
|
|
if( boxesPerStud == 0 && i > yMinInt )
|
|
i = yMaxInt;
|
|
ptYDir.y = (float)i;
|
|
Vector3 ptInWorld = planeCoord.pointToWorldSpace(ptYDir);
|
|
adornCoord.translation = ptInWorld;
|
|
cylinder(adorn, adornCoord, direction2, (float)(xMaxInt - xMinInt), 0.03, color);
|
|
if( i < yMaxInt )
|
|
{
|
|
for( int j = 1; j < boxesPerStud; j++ )
|
|
{
|
|
ptYDir.y = (float)i + (1.0f/(float)boxesPerStud) * (float)j;
|
|
ptInWorld = planeCoord.pointToWorldSpace(ptYDir);
|
|
adornCoord.translation = ptInWorld;
|
|
cylinder(adorn, adornCoord, direction2, (float)(xMaxInt - xMinInt), 0.01, color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void DrawAdorn::axisWidget(Adorn* adorn, const RBX::Camera& camera)
|
|
{
|
|
Rect2D viewport = adorn->getViewport();
|
|
|
|
float sizeOfWidget = 0.1f;
|
|
Vector3 offset = Vector3((viewport.width()/2) - 50,(-viewport.height()/2) + 50,0);
|
|
Vector3 widgetOrigin3dPos = camera.getRenderingCoordinateFrame().translation + (camera.getRenderingCoordinateFrame().lookVector() * 2);
|
|
Vector3 widgetOrigin2dPos = camera.project(widgetOrigin3dPos) - offset;
|
|
|
|
Vector3 widgetXPos = camera.project(widgetOrigin3dPos + Vector3::unitX() * sizeOfWidget) - offset;
|
|
adorn->line2d(Vector2(widgetOrigin2dPos.x,widgetOrigin2dPos.y),Vector2(widgetXPos.x,widgetXPos.y),axisColors[0]);
|
|
adorn->drawFont2D(
|
|
"X",
|
|
Vector2(widgetXPos.x,widgetXPos.y),
|
|
12.0f,
|
|
false,
|
|
axisColors[0],
|
|
RBX::Color4::clear(),
|
|
RBX::Text::FONT_ARIAL );
|
|
|
|
Vector3 widgetYPos = camera.project(widgetOrigin3dPos + Vector3::unitY() * sizeOfWidget) - offset;
|
|
adorn->line2d(Vector2(widgetOrigin2dPos.x,widgetOrigin2dPos.y),Vector2(widgetYPos.x,widgetYPos.y),axisColors[1]);
|
|
adorn->drawFont2D(
|
|
"Y",
|
|
Vector2(widgetYPos.x + 3.0f,widgetYPos.y),
|
|
12.0f,
|
|
false,
|
|
axisColors[1],
|
|
RBX::Color4::clear(),
|
|
RBX::Text::FONT_ARIAL );
|
|
|
|
Vector3 widgetZPos = camera.project(widgetOrigin3dPos + Vector3::unitZ() * sizeOfWidget) - offset;
|
|
adorn->line2d(Vector2(widgetOrigin2dPos.x,widgetOrigin2dPos.y),Vector2(widgetZPos.x,widgetZPos.y),axisColors[2]);
|
|
adorn->drawFont2D(
|
|
"Z",
|
|
Vector2(widgetZPos.x,widgetZPos.y),
|
|
12.0f,
|
|
false,
|
|
axisColors[2],
|
|
RBX::Color4::clear(),
|
|
RBX::Text::FONT_ARIAL );
|
|
}
|
|
|
|
void DrawAdorn::circularGridAtCoord(
|
|
Adorn* adorn,
|
|
const CoordinateFrame& coordFrame,
|
|
const G3D::Vector3& size,
|
|
const G3D::Vector3& cameraPos,
|
|
NormalId normalId,
|
|
const Color4& color,
|
|
int boxesPerStud )
|
|
{
|
|
const Vector3 halfSize = size / 2;
|
|
const Extents localExtents = Extents(-halfSize,halfSize);
|
|
const Vector3 handlePos = handlePosInObject(
|
|
cameraPos,
|
|
localExtents,
|
|
HANDLE_ROTATE,
|
|
normalId );
|
|
const Vector3 handlePosInWorld = coordFrame.pointToWorldSpace(handlePos);
|
|
const Vector3 delta = coordFrame.translation - handlePosInWorld;
|
|
const float radius = delta.length();
|
|
const Vector3 normal = normalIdToVector3(normalId);
|
|
const int direction = (normalId == NORM_X || normalId == NORM_X_NEG ) ? 1 : 0;
|
|
const float rotationAngle = Math::pif() / boxesPerStud;
|
|
const Matrix3 rotation_increment = Matrix3::fromAxisAngle(normal,rotationAngle);
|
|
const float line_thickness = scaleRelativeToCamera(
|
|
cameraPos,
|
|
coordFrame.translation,
|
|
RotationGridLineMinAngle,
|
|
RotationGridLineThickness );
|
|
|
|
adorn->setMaterial(Adorn::Material_SelfLit);
|
|
|
|
CoordinateFrame frame = coordFrame;
|
|
for ( int i = 0 ; i < boxesPerStud ; ++i )
|
|
{
|
|
frame.rotation *= rotation_increment;
|
|
cylinder(
|
|
adorn,
|
|
frame,
|
|
direction,
|
|
radius * 2,
|
|
line_thickness,
|
|
color );
|
|
}
|
|
|
|
adorn->setMaterial(Adorn::Material_Default);
|
|
}
|
|
|
|
void DrawAdorn::lineSegmentRelativeToCoord(
|
|
Adorn* adorn,
|
|
const CoordinateFrame& cF,
|
|
const Vector3& pt0,
|
|
const Vector3& pt1,
|
|
const G3D::Color3& color,
|
|
float lineThickness )
|
|
{
|
|
CoordinateFrame lineCf;
|
|
|
|
Vector3 lineVector = pt1 - pt0;
|
|
lineCf.translation = pt0 + lineVector * 0.5f;
|
|
float lineLength = lineVector.magnitude();
|
|
lineCf.rotation = Math::getWellFormedRotForZVector(lineVector/lineLength);
|
|
|
|
DrawAdorn::cylinder(adorn, cF*lineCf, 2, lineLength, lineThickness, color);
|
|
}
|
|
|
|
void DrawAdorn::verticalLineSegmentSplit(
|
|
Adorn* adorn,
|
|
const CoordinateFrame& cF,
|
|
const Vector3& pt0,
|
|
const Vector3& pt1,
|
|
const float& delta,
|
|
const float& dropFactor,
|
|
const short& level,
|
|
const G3D::Color3& color,
|
|
float lineThickness )
|
|
{
|
|
short maxLevels = 3;
|
|
float recursiveDropFactor = 0.25f;
|
|
Vector3 splitPoint = 0.5 * (pt1 - pt0) + pt0;
|
|
splitPoint.y -= dropFactor * delta;
|
|
|
|
if( level < maxLevels )
|
|
{
|
|
verticalLineSegmentSplit(adorn, cF, pt0, splitPoint, delta, recursiveDropFactor*dropFactor, level+1, color, lineThickness);
|
|
verticalLineSegmentSplit(adorn, cF, splitPoint, pt1, delta, recursiveDropFactor*dropFactor, level+1, color, lineThickness);
|
|
}
|
|
else
|
|
{
|
|
lineSegmentRelativeToCoord(adorn, cF, pt0, splitPoint, color, lineThickness);
|
|
lineSegmentRelativeToCoord(adorn, cF, pt1, splitPoint, color, lineThickness);
|
|
}
|
|
|
|
}
|
|
|
|
void DrawAdorn::surfacePolygon(
|
|
Adorn* adorn,
|
|
PartInstance& partInst,
|
|
int surfaceId,
|
|
const G3D::Color3& color,
|
|
float lineThickness )
|
|
{
|
|
std::vector<Vector3> polygonInPart;
|
|
for( int i = 0; i < PartInstance::getConstPrimitive(&partInst)->getConstGeometry()->getNumVertsInSurface(surfaceId); i++ )
|
|
polygonInPart.push_back(PartInstance::getConstPrimitive(&partInst)->getConstGeometry()->getSurfaceVertInBody(surfaceId, i));
|
|
|
|
DrawAdorn::polygonRelativeToCoord(adorn, PartInstance::getConstPrimitive(&partInst)->getCoordinateFrame(), polygonInPart, color, lineThickness);
|
|
}
|
|
|
|
void DrawAdorn::polygonRelativeToCoord(
|
|
Adorn* adorn,
|
|
const CoordinateFrame& cF,
|
|
std::vector<Vector3>& vertices,
|
|
const G3D::Color4& color,
|
|
float lineThickness )
|
|
{
|
|
CoordinateFrame lineCf;
|
|
|
|
for(unsigned int i = 0; i < vertices.size(); i++ )
|
|
{
|
|
int next = i < vertices.size()-1 ? i + 1 : 0;
|
|
Vector3 lineVector = vertices[next] - vertices[i];
|
|
lineCf.translation = vertices[i] + lineVector * 0.5f;
|
|
float lineLength = lineVector.magnitude();
|
|
lineCf.rotation = Math::getWellFormedRotForZVector(lineVector/lineLength);
|
|
|
|
cylinder(adorn, cF*lineCf, 2, lineLength, lineThickness, color);
|
|
}
|
|
}
|
|
|
|
|
|
void DrawAdorn::partSurface(
|
|
const Part& part,
|
|
int surfaceId,
|
|
Adorn* adorn,
|
|
const Color4& color,
|
|
float thickness)
|
|
{
|
|
adorn->setObjectToWorldMatrix(part.coordinateFrame);
|
|
|
|
Vector3 halfRealSize = 0.5f * part.gridSize;
|
|
surfaceBorder(adorn, halfRealSize, thickness, surfaceId, color);
|
|
}
|
|
|
|
Vector3 DrawAdorn::handlePosInObject(
|
|
const Vector3& cameraPos,
|
|
const Extents& localExtents,
|
|
HandleType handleType,
|
|
NormalId normalId )
|
|
{
|
|
const Vector3 size = localExtents.size();
|
|
const Vector3 halfSize = size / 2;
|
|
|
|
switch ( handleType )
|
|
{
|
|
case HANDLE_ROTATE:
|
|
{
|
|
const float halfdiagonal = halfSize.length();
|
|
const float default_radius = halfdiagonal + HandleOffset * 4;
|
|
|
|
// TODO fix this so we can have a nice scaled circle far away
|
|
// use the default radius because if we scale it's gonna get huge
|
|
// need to determine circle size on screen and make sure it's larger
|
|
// than the minimum size on screen
|
|
const float radius = default_radius;
|
|
// const float radius = scaleRelativeToCamera(
|
|
// cameraPos,
|
|
// localExtents.center(),
|
|
// TorusMinAngle,
|
|
// default_radius );
|
|
|
|
const Vector3 axis = normalIdToVector3(normalIdToU(normalId));
|
|
return localExtents.center() + axis * radius;
|
|
}
|
|
case HANDLE_RESIZE:
|
|
{
|
|
const Vector3 axis = normalIdToVector3(normalId);
|
|
const Vector3 start_offset(HandleOffset,HandleOffset,HandleOffset);
|
|
return localExtents.center() + axis * (halfSize + start_offset * 4);
|
|
}
|
|
case HANDLE_VELOCITY:
|
|
case HANDLE_MOVE:
|
|
{
|
|
const Vector3 axis = normalIdToVector3(normalId);
|
|
const Vector3 start_offset(HandleOffset,HandleOffset,HandleOffset);
|
|
return localExtents.center() + axis * (halfSize + start_offset);
|
|
}
|
|
default:
|
|
RBXASSERT(false);
|
|
return Vector3::zero();
|
|
}
|
|
}
|
|
|
|
float DrawAdorn::scaleHandleRelativeToCamera(
|
|
const Vector3& cameraPos,
|
|
HandleType handleType,
|
|
const Vector3& handlePos )
|
|
{
|
|
float min_angle;
|
|
float expected_radius;
|
|
|
|
switch ( handleType )
|
|
{
|
|
case HANDLE_RESIZE:
|
|
min_angle = SphereMinAngle;
|
|
expected_radius = SphereRadius;
|
|
break;
|
|
case HANDLE_MOVE:
|
|
min_angle = ArrowMinAngle;
|
|
expected_radius = ArrowLength;
|
|
break;
|
|
case HANDLE_ROTATE:
|
|
min_angle = SphereMinAngle;
|
|
expected_radius = SphereRadius;
|
|
break;
|
|
case HANDLE_VELOCITY:
|
|
min_angle = SphereMinAngle;
|
|
expected_radius = SphereRadius;
|
|
break;
|
|
}
|
|
|
|
return scaleRelativeToCamera(
|
|
cameraPos,
|
|
handlePos,
|
|
min_angle,
|
|
expected_radius );
|
|
}
|
|
|
|
// visual angle = radius/distance
|
|
float DrawAdorn::scaleRelativeToCamera(
|
|
const Vector3& cameraPos,
|
|
const Vector3& handlePos,
|
|
float minAngle,
|
|
float expectedSize )
|
|
{
|
|
const float distance = (cameraPos - handlePos).magnitude();
|
|
|
|
float scale = 1.0f;
|
|
if ( distance > (1.0f / minAngle) )
|
|
scale = distance * minAngle;
|
|
|
|
const float radius = scale * expectedSize;
|
|
return radius;
|
|
}
|
|
|
|
void DrawAdorn::handles2d(
|
|
const G3D::Vector3& size,
|
|
const G3D::CoordinateFrame& position,
|
|
const RBX::Camera& camera,
|
|
Adorn* adorn,
|
|
HandleType handleType,
|
|
const G3D::Color4& color,
|
|
bool useAxisColor,
|
|
int normalIdMask)
|
|
{
|
|
Vector3 halfSize = size / 2;
|
|
RBX::Extents localExtents(-halfSize,halfSize);
|
|
|
|
for (int posNeg = 0; posNeg < 2; ++posNeg)
|
|
{
|
|
for (int xyz = 0; xyz < 3; ++xyz)
|
|
{
|
|
if (normalIdMask & (1<<(xyz + (posNeg*3))))
|
|
{
|
|
NormalId normalId = (NormalId)(posNeg*3 + xyz);
|
|
Vector3 handlePos = handlePosInObject(
|
|
camera.coordinateFrame().translation,
|
|
localExtents,
|
|
handleType,
|
|
normalId );
|
|
|
|
Vector3 handlePosInWorld = position.pointToWorldSpace(handlePos);
|
|
|
|
if (handleType == HANDLE_MOVE)
|
|
{
|
|
// compute relative size on screen based on distance
|
|
const float handleRadius = scaleRelativeToCamera(
|
|
camera.coordinateFrame().translation,
|
|
handlePosInWorld,
|
|
ArrowMinAngle,
|
|
ArrowLength-0.5f);
|
|
|
|
Vector3 correctedHandlePos = normalIdToVector3(normalId) * handleRadius;
|
|
|
|
//transform point back to world
|
|
CoordinateFrame frame(position.rotation,handlePosInWorld);
|
|
handlePosInWorld = frame.pointToWorldSpace(correctedHandlePos);
|
|
}
|
|
|
|
Vector3 screenPos = camera.project(handlePosInWorld);
|
|
|
|
if (screenPos.z == std::numeric_limits<float>::infinity())
|
|
return;
|
|
|
|
adorn->rect2d(Rect::fromCenterSize(screenPos.xy(), 6.0f).toRect2D(),
|
|
useAxisColor ? axisColors[xyz] : color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DrawAdorn::partInfoText2D(
|
|
const G3D::Vector3& size,
|
|
const G3D::CoordinateFrame& position,
|
|
const RBX::Camera& camera,
|
|
Adorn* adorn,
|
|
const std::string& text,
|
|
const G3D::Color4& color,
|
|
float fontSize,
|
|
int normalIdMask)
|
|
{
|
|
Vector3 halfSize = size / 2;
|
|
RBX::Extents localExtents(-halfSize,halfSize);
|
|
|
|
const Vector3 max = localExtents.max();
|
|
const Vector3 min = localExtents.min();
|
|
|
|
Vector3 points[8];
|
|
points[0] = Vector3(min.x, min.y, min.z);
|
|
points[1] = Vector3(min.x, max.y, min.z);
|
|
points[2] = Vector3(min.x, max.y, max.z);
|
|
points[3] = Vector3(min.x, min.y, max.z);
|
|
|
|
points[4] = Vector3(max.x, min.y, min.z);
|
|
points[5] = Vector3(max.x, max.y, min.z);
|
|
points[6] = Vector3(max.x, max.y, max.z);
|
|
points[7] = Vector3(max.x, min.y, max.z);
|
|
|
|
Vector2 lowestRightCornerMax = Vector2(0.0f, 0.0f);
|
|
float minDistanceToOptimal = FLT_MAX;
|
|
Vector2 lowestRightCorner;
|
|
|
|
// In projection space, find the optimal point, the lowest rightest maximum of all the points.
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
const Vector3 worldPos = position.pointToWorldSpace(points[i]);
|
|
const Vector3 projPos = camera.project(worldPos);
|
|
|
|
points[i] = projPos;
|
|
|
|
if (projPos.z == std::numeric_limits<float>::infinity())
|
|
continue;
|
|
|
|
if (projPos.y > lowestRightCornerMax.y)
|
|
{
|
|
lowestRightCornerMax.y = projPos.y;
|
|
}
|
|
|
|
if (projPos.x > lowestRightCornerMax.x)
|
|
{
|
|
lowestRightCornerMax.x = projPos.x;
|
|
}
|
|
}
|
|
|
|
// Find the point closest to lowest rightest maximum.
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
const Vector3 projPos = points[i];
|
|
|
|
if (projPos.z == std::numeric_limits<float>::infinity())
|
|
continue;
|
|
|
|
float distanceToOptimal = (projPos.xy() - lowestRightCornerMax).length();
|
|
|
|
if (minDistanceToOptimal > distanceToOptimal)
|
|
{
|
|
minDistanceToOptimal = distanceToOptimal;
|
|
lowestRightCorner = projPos.xy();
|
|
}
|
|
}
|
|
|
|
adorn->drawFont2D(text,
|
|
lowestRightCorner,
|
|
fontSize,
|
|
false,
|
|
color,
|
|
RBX::Color4::clear(),
|
|
RBX::Text::FONT_ARIAL);
|
|
}
|
|
|
|
void DrawAdorn::handles3d(
|
|
const G3D::Vector3& size,
|
|
const G3D::CoordinateFrame& position,
|
|
Adorn* adorn,
|
|
HandleType handleType,
|
|
const Vector3& cameraPos,
|
|
const G3D::Color4& color,
|
|
bool useAxisColor,
|
|
int normalIdMask,
|
|
NormalId normalIdTohighlight,
|
|
const G3D::Color4& highlightColor)
|
|
{
|
|
const Vector3 halfSize = size / 2;
|
|
const Extents localExtents(-halfSize,halfSize);
|
|
|
|
int usedaxes = 0; // for rotation handles, prevent use of NORM_X and NORM_X_NEG at the same time.
|
|
for (int posNeg = 0; posNeg < 2; ++posNeg)
|
|
{
|
|
for (int xyz = 0; xyz < 3; ++xyz)
|
|
{
|
|
if(normalIdMask & (1<<(xyz + (posNeg*3))))
|
|
{
|
|
NormalId normalId = intToNormalId(posNeg * 3 + xyz);
|
|
|
|
// determine where the handle should be located
|
|
const Vector3 handlePos = handlePosInObject(
|
|
cameraPos,
|
|
localExtents,
|
|
handleType,
|
|
normalId );
|
|
const Vector3 handlePosInWorld = position.pointToWorldSpace(handlePos);
|
|
|
|
// compute relative size on screen based on distance
|
|
const float handleRadius = scaleHandleRelativeToCamera(
|
|
cameraPos,
|
|
handleType,
|
|
handlePosInWorld );
|
|
|
|
const CoordinateFrame frame(position.rotation,handlePosInWorld);
|
|
adorn->setObjectToWorldMatrix(frame);
|
|
|
|
if ( normalIdTohighlight == normalId )
|
|
adorn->setMaterial(Adorn::Material_SelfLitHighlight);
|
|
else
|
|
adorn->setMaterial(Adorn::Material_SelfLit);
|
|
|
|
const int axis_index = normalId % 3;
|
|
Color4 handleColor = useAxisColor ? axisColors[axis_index] : color;
|
|
|
|
if (normalIdTohighlight != normalId)
|
|
handleColor.a *= HandleTransparency;
|
|
|
|
switch (handleType)
|
|
{
|
|
case HANDLE_RESIZE:
|
|
case HANDLE_VELOCITY:
|
|
{
|
|
if (normalIdTohighlight == normalId)
|
|
adorn->sphere(Sphere(Vector3::zero(),handleRadius+0.08), handleColor);
|
|
else
|
|
adorn->sphere(Sphere(Vector3::zero(),handleRadius), handleColor);
|
|
break;
|
|
}
|
|
case HANDLE_MOVE:
|
|
{
|
|
Vector3 direction = normalIdToVector3(normalId) * handleRadius;
|
|
RbxRay rayInPart = RbxRay::fromOriginAndDirection(Vector3::zero(),direction);
|
|
adorn->ray(rayInPart, handleColor);
|
|
break;
|
|
}
|
|
case HANDLE_ROTATE:
|
|
{
|
|
const int axis_flag = normalIdToMask(normalId);
|
|
|
|
// only allow if positive orientation wasn't already specified.
|
|
if ( (usedaxes & axis_flag) == 0 )
|
|
{
|
|
const Vector3 delta = position.translation - handlePosInWorld;
|
|
const float torus_radius = delta.length();
|
|
|
|
// compute relative size thickness based on distance
|
|
const float thickness = scaleRelativeToCamera(
|
|
cameraPos,
|
|
position.translation,
|
|
TorusThicknessMinAngle,
|
|
TorusThickness );
|
|
|
|
Color4 axisColor(handleColor);
|
|
|
|
if (normalIdTohighlight != normalId)
|
|
axisColor.a = AxisTransparency;
|
|
|
|
torus(
|
|
adorn,
|
|
position,
|
|
normalId,
|
|
torus_radius,
|
|
thickness,
|
|
axisColor);
|
|
|
|
usedaxes |= axis_flag;
|
|
}
|
|
|
|
// need to rest the matrix because torus changed it
|
|
adorn->setObjectToWorldMatrix(handlePosInWorld);
|
|
if (normalIdTohighlight == normalId)
|
|
adorn->sphere( Sphere(Vector3::zero(),handleRadius+0.08),
|
|
handleColor);
|
|
else
|
|
adorn->sphere(Sphere(Vector3::zero(),handleRadius), Color3(handleColor.r - 0.02, handleColor.g - 0.02, handleColor.b - 0.02));
|
|
break;
|
|
}
|
|
default:
|
|
RBXASSERT(false);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
adorn->setMaterial(Adorn::Material_Default);
|
|
}
|
|
|
|
// circle, with normal of linespace parallel to radius.
|
|
class CircleRadialNormal : public I3DLinearFunc
|
|
{
|
|
Vector3 x;
|
|
Vector3 y;
|
|
Vector3 z;
|
|
float r;
|
|
NormalId axis;
|
|
public:
|
|
CircleRadialNormal(float radius, NormalId axis) : r(radius), axis(axis)
|
|
{
|
|
x = uvwToObject(Vector3::unitX(), axis);
|
|
y = uvwToObject(Vector3::unitY(), axis);
|
|
z = uvwToObject(Vector3::unitZ(), axis);
|
|
}
|
|
Vector3 eval(float t)
|
|
{
|
|
double theta = t *2* G3D::pi();
|
|
return x*(float)(cos(theta)*r) + y*(float)(sin(theta)*r);
|
|
}
|
|
// first derivative.
|
|
Vector3 evalTangent(float t)
|
|
{
|
|
double theta = t *2* G3D::pi();
|
|
return y*(float)cos(theta) - x*(float)sin(theta);
|
|
}
|
|
Vector3 evalNormal(float t)
|
|
{
|
|
double theta = t *2* G3D::pi();
|
|
return x*(float)cos(theta) + y*(float)sin(theta);
|
|
}
|
|
Vector3 evalBinormal(float t)
|
|
{
|
|
return -z;
|
|
}
|
|
|
|
//string that encodes this function in a unique way.
|
|
std::string hashString()
|
|
{
|
|
std::ostringstream hash;
|
|
hash << "CircleRN(" << r << "," << axis << ")";
|
|
return hash.str();
|
|
}
|
|
};
|
|
|
|
void DrawAdorn::chatBubble2d(
|
|
Adorn* adorn,
|
|
const Rect2D& rect,
|
|
const G3D::Vector2 pointer,
|
|
float cornerradius,
|
|
const float linewidth,
|
|
const int quarterdivs,
|
|
const Color4& color)
|
|
{
|
|
cornerradius = std::min(cornerradius, std::min(rect.width(), rect.height()));
|
|
const float pointerwidth = std::min(cornerradius * 1.5f, (rect.width() - cornerradius * 2.0f));
|
|
|
|
std::vector<G3D::Vector2> verts;
|
|
std::vector<G3D::Vector2> vertsBorder;
|
|
|
|
// start at base, right side of pointer.
|
|
// go counterclockwise.
|
|
// ui space: math space:
|
|
// (0,0)
|
|
// /--------------\ n
|
|
// | | \
|
|
// | | n-1\ \ 0 1
|
|
// \-----/ /-----/ /------\ \-----\
|
|
// n-1/ / 0 1 | |
|
|
// / | |
|
|
// n \---------------/
|
|
// (0,0)
|
|
|
|
// y1 should be bottom.
|
|
Vector2 p0(rect.center().x + pointerwidth/2, rect.y1());
|
|
Vector2 p1(rect.center().x - pointerwidth/2, rect.y1());
|
|
verts.push_back(p0);
|
|
vertsBorder.push_back(p0 + Vector2(linewidth, linewidth));
|
|
|
|
|
|
RBXASSERT(rect.wh().x >= cornerradius *2);
|
|
RBXASSERT(rect.wh().y >= cornerradius * 2);
|
|
|
|
Rect2D innerrect = rect.border(cornerradius);
|
|
|
|
double angleinc = (quarterdivs > 0) ? (-(Math::pi() * 0.5) / quarterdivs) : 0;
|
|
for(int i = 0; i <= quarterdivs; ++i)
|
|
{
|
|
double angle = (quarterdivs > 0) ? (Math::pi() * 0.5 + angleinc * i) : (Math::pi() * 0.25);
|
|
Vector2 r = Vector2((float)cos(angle), (quarterdivs > 0) ? (float)sin(angle) : 1.0f);
|
|
verts.push_back(innerrect.x1y1() + r * cornerradius);
|
|
vertsBorder.push_back(innerrect.x1y1() + r * (cornerradius + linewidth));
|
|
}
|
|
for(int i = 0; i <= quarterdivs; ++i)
|
|
{
|
|
double angle = (quarterdivs > 0) ? (Math::pi() * 0.0 + angleinc * i) : (Math::pi() * 1.75);
|
|
Vector2 r = Vector2((float)cos(angle), (float)sin(angle));
|
|
verts.push_back(innerrect.x1y0() + r * cornerradius);
|
|
vertsBorder.push_back(innerrect.x1y0() + r * (cornerradius + linewidth));
|
|
}
|
|
for(int i = 0; i <= quarterdivs; ++i)
|
|
{
|
|
double angle = (quarterdivs > 0) ? (Math::pi() * 1.5 + angleinc * i) : (Math::pi() * 1.25);
|
|
Vector2 r = Vector2((float)cos(angle), (float)sin(angle));
|
|
verts.push_back(innerrect.x0y0() + r * cornerradius);
|
|
vertsBorder.push_back(innerrect.x0y0() + r * (cornerradius + linewidth));
|
|
}
|
|
for(int i = 0; i <= quarterdivs; ++i)
|
|
{
|
|
double angle = (quarterdivs > 0) ? (Math::pi() * 1.0 + angleinc * i) : (Math::pi() * .75);
|
|
Vector2 r = Vector2((float)cos(angle), (quarterdivs > 0) ? (float)sin(angle) : 1.0f);
|
|
verts.push_back(innerrect.x0y1() + r * cornerradius);
|
|
vertsBorder.push_back(innerrect.x0y1() + r * (cornerradius + linewidth));
|
|
}
|
|
verts.push_back(p1);
|
|
vertsBorder.push_back(p1 + Vector2(-linewidth, linewidth));
|
|
verts.push_back(pointer);
|
|
Vector2 pointerdir = pointer - (p0+p1)*0.5f;
|
|
pointerdir.unitize();
|
|
|
|
vertsBorder.push_back(pointer + pointerdir * linewidth*3); // todo: not correct.
|
|
|
|
adorn->convexPolygon2d(&vertsBorder[0], vertsBorder.size(), G3D::Color4(Color3::black()));
|
|
adorn->convexPolygon2d(&verts[0], verts.size(), G3D::Color4(color));
|
|
}
|
|
|
|
void DrawAdorn::torus(
|
|
Adorn* adorn,
|
|
const CoordinateFrame& position,
|
|
NormalId axis,
|
|
float radius,
|
|
float thicknessradius,
|
|
const Color4& color)
|
|
{
|
|
CircleRadialNormal trajectory(radius, axis);
|
|
CircleRadialNormal profile(thicknessradius, NORM_Z);
|
|
|
|
adorn->setObjectToWorldMatrix(position);
|
|
adorn->extrusion(&trajectory, 32, &profile, 8, color);
|
|
}
|
|
|
|
/**
|
|
* Draws a wire star made up of 3 line segments.
|
|
*
|
|
* @param adorn object used to generate the geometry
|
|
* @param center world space center coordinates
|
|
* @param size lines are -size to +size on the axis
|
|
* @param colorX color of x axis line
|
|
* @param colorY color of y axis line
|
|
* @param colorZ color of z axis line
|
|
*/
|
|
void DrawAdorn::star(
|
|
Adorn* adorn,
|
|
const Vector3& center,
|
|
float size,
|
|
const Color4& colorX,
|
|
const Color4& colorY,
|
|
const Color4& colorZ )
|
|
{
|
|
size /= 2.0f;
|
|
|
|
adorn->line3d(
|
|
center - Vector3::unitX() * size,
|
|
center + Vector3::unitX() * size,
|
|
colorX );
|
|
adorn->line3d(
|
|
center - Vector3::unitY() * size,
|
|
center + Vector3::unitY() * size,
|
|
colorY );
|
|
adorn->line3d(
|
|
center - Vector3::unitZ() * size,
|
|
center + Vector3::unitZ() * size,
|
|
colorZ );
|
|
}
|
|
|
|
/**
|
|
* Draw an outline (wireframe) box in 3D.
|
|
*
|
|
* @param adorn adornment to use for drawing
|
|
* @param box box to draw
|
|
* @param color color of lines
|
|
*/
|
|
void DrawAdorn::outlineBox(
|
|
Adorn* adorn,
|
|
const AABox& box,
|
|
const Color4& color )
|
|
{
|
|
const Vector3& p0 = box.low();
|
|
const Vector3& p1 = box.high();
|
|
|
|
const float X0 = p0.x;
|
|
const float Y0 = p0.y;
|
|
const float Z0 = p0.z;
|
|
const float X1 = p1.x;
|
|
const float Y1 = p1.y;
|
|
const float Z1 = p1.z;
|
|
|
|
adorn->line3d(
|
|
Vector3(X0,Y0,Z0),
|
|
Vector3(X1,Y0,Z0),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X0,Y1,Z0),
|
|
Vector3(X1,Y1,Z0),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X0,Y0,Z1),
|
|
Vector3(X1,Y0,Z1),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X0,Y1,Z1),
|
|
Vector3(X1,Y1,Z1),
|
|
color );
|
|
|
|
adorn->line3d(
|
|
Vector3(X0,Y0,Z0),
|
|
Vector3(X0,Y1,Z0),
|
|
color);
|
|
adorn->line3d(
|
|
Vector3(X1,Y0,Z0),
|
|
Vector3(X1,Y1,Z0),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X0,Y0,Z1),
|
|
Vector3(X0,Y1,Z1),
|
|
color);
|
|
adorn->line3d(
|
|
Vector3(X1,Y0,Z1),
|
|
Vector3(X1,Y1,Z1),
|
|
color );
|
|
|
|
adorn->line3d(
|
|
Vector3(X0,Y0,Z0),
|
|
Vector3(X0,Y0,Z1),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X1,Y0,Z0),
|
|
Vector3(X1,Y0,Z1),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X0,Y1,Z0),
|
|
Vector3(X0,Y1,Z1),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X1,Y1,Z0),
|
|
Vector3(X1,Y1,Z1),
|
|
color );
|
|
}
|
|
|
|
/**
|
|
* Draw select indicator around a box similar to 3DS.
|
|
*
|
|
* @param adorn adornment to use for drawing
|
|
* @param box box to draw
|
|
* @param color color of lines
|
|
*/
|
|
void DrawAdorn::selectionBox(
|
|
Adorn* adorn,
|
|
const AABox& box,
|
|
const Color4& color )
|
|
{
|
|
const Vector3& P0 = box.low();
|
|
const Vector3& P1 = box.high();
|
|
|
|
float X0 = P0.x;
|
|
float Y0 = P0.y;
|
|
float Z0 = P0.z;
|
|
float X1 = P1.x;
|
|
float Y1 = P1.y;
|
|
float Z1 = P1.z;
|
|
|
|
float s0 = X0 + (X1 - X0) / 4;
|
|
float s1 = X1 + (X0 - X1) / 4;
|
|
float s2 = Y0 + (Y1 - Y0) / 4;
|
|
float s3 = Y1 + (Y0 - Y1) / 4;
|
|
float s4 = Z0 + (Z1 - Z0) / 4;
|
|
float s5 = Z1 + (Z0 - Z1) / 4;
|
|
|
|
// side0
|
|
adorn->line3d(
|
|
Vector3(X0,Y0,Z0),
|
|
Vector3(s0,Y0,Z0),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X0,Y1,Z0),
|
|
Vector3(s0,Y1,Z0),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X0,Y0,Z1),
|
|
Vector3(s0,Y0,Z1),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X0,Y1,Z1),
|
|
Vector3(s0,Y1,Z1),
|
|
color );
|
|
|
|
// side1
|
|
adorn->line3d(
|
|
Vector3(X1,Y0,Z0),
|
|
Vector3(s1,Y0,Z0),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X1,Y1,Z0),
|
|
Vector3(s1,Y1,Z0),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X1,Y0,Z1),
|
|
Vector3(s1,Y0,Z1),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X1,Y1,Z1),
|
|
Vector3(s1,Y1,Z1),
|
|
color );
|
|
|
|
// side2
|
|
adorn->line3d(
|
|
Vector3(X0,Y0,Z0),
|
|
Vector3(X0,s2,Z0),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X1,Y0,Z0),
|
|
Vector3(X1,s2,Z0),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X0,Y0,Z1),
|
|
Vector3(X0,s2,Z1),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X1,Y0,Z1),
|
|
Vector3(X1,s2,Z1),
|
|
color );
|
|
|
|
// side3
|
|
adorn->line3d(
|
|
Vector3(X0,Y1,Z0),
|
|
Vector3(X0,s3,Z0),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X1,Y1,Z0),
|
|
Vector3(X1,s3,Z0),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X0,Y1,Z1),
|
|
Vector3(X0,s3,Z1),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X1,Y1,Z1),
|
|
Vector3(X1,s3,Z1),
|
|
color );
|
|
|
|
// side4
|
|
adorn->line3d(
|
|
Vector3(X0,Y0,Z0),
|
|
Vector3(X0,Y0,s4),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X1,Y0,Z0),
|
|
Vector3(X1,Y0,s4),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X0,Y1,Z0),
|
|
Vector3(X0,Y1,s4),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X1,Y1,Z0),
|
|
Vector3(X1,Y1,s4),
|
|
color );
|
|
|
|
// side5
|
|
adorn->line3d(
|
|
Vector3(X0,Y0,Z1),
|
|
Vector3(X0,Y0,s5),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X1,Y0,Z1),
|
|
Vector3(X1,Y0,s5),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X0,Y1,Z1),
|
|
Vector3(X0,Y1,s5),
|
|
color );
|
|
adorn->line3d(
|
|
Vector3(X1,Y1,Z1),
|
|
Vector3(X1,Y1,s5),
|
|
color );
|
|
}
|
|
|
|
} // namespace
|