Files
2025-09-18 17:55:52 -04:00

197 lines
5.6 KiB
C++

/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */
#include "AppDraw/Draw.h"
#include "AppDraw/DrawPrimitives.h"
#include "AppDraw/DrawAdorn.h"
#include "GfxBase/Part.h"
#include "GfxBase/Adorn.h"
#include "Util/Math.h"
#include "V8DataModel/PartInstance.h"
namespace RBX {
static const int SelectionBoxLineThreshold = 1500; //!< tweakable threshold before boxes switch to lines
bool Draw::m_showHoverOver = true;
G3D::Color4 Draw::m_hoverOverColor(0.7f,0.9f,1.0f,1.0f); // 178, 229, 255
G3D::Color4 Draw::m_selectColor(0.1f,0.6f,1.0f,1.0f); // 25, 153, 256
void Draw::partAdorn(
const Part& part,
Adorn* adorn,
const G3D::Color3& controllerColor)
{
adornSurfaces(part, adorn, controllerColor);
}
/**
* Set color used for the selection box when a part is selected.
*/
void Draw::setSelectColor(const G3D::Color4& Color)
{
m_selectColor = Color;
}
/**
* Set the color used for the selection box when a part is mouse hover over.
*/
void Draw::setHoverOverColor(const G3D::Color4& color)
{
m_hoverOverColor = color;
}
void Draw::selectionBox(
const Part& part,
Adorn* adorn,
SelectState selectState,
float lineThickness)
{
if ( selectState == SELECT_HOVER && m_showHoverOver )
{
adorn->setObjectToWorldMatrix(part.coordinateFrame);
selectionBox(
part,
adorn,
m_hoverOverColor,
lineThickness * 2 );
}
else if ( selectState != SELECT_HOVER )
{
selectionBox(
part,
adorn,
(selectState == SELECT_NORMAL) ? selectColor() : Color3::orange(),
lineThickness );
}
}
void Draw::selectionBox(
ModelInstance& model,
Adorn* adorn,
const G3D::Color4& selectColor,
float lineThickness)
{
selectionBox(model.computePart(), adorn, selectColor, lineThickness);
}
void Draw::adornSurfaces(const Part& part,
Adorn* adorn,
const G3D::Color3& controllerColor)
{
for (int face = 0; face < 6; ++face) {
switch (part.surfaceType[face])
{
default: break;
case ROTATE:
case ROTATE_P:
case ROTATE_V: Draw::constraint(part, adorn, face, controllerColor); break;
}
}
}
void Draw::constraint(const Part& part,
Adorn* adorn,
int face,
const G3D::Color3& controllerColor)
{
SurfaceType surfaceType = part.surfaceType[face];
debugAssert( (surfaceType == ROTATE)
|| (surfaceType == ROTATE_P)
|| (surfaceType == ROTATE_V));
Vector3 halfSize = part.gridSize * 0.5;
const Matrix3& relativeRotation = Math::getAxisRotationMatrix(face);
Vector3 relativeTranslation;
int normal = face % 3;
float posNeg = face > 2 ? -1.0f : 1.0f;
relativeTranslation[normal] = halfSize[normal] * posNeg;
CoordinateFrame translation(relativeRotation, relativeTranslation);
CoordinateFrame newObject = part.coordinateFrame * translation;
adorn->setObjectToWorldMatrix(newObject);
float axis = 1.0f;
float radius = 0.2f;
adorn->cylinderAlongX(radius, axis, Color3::yellow());
if ((surfaceType == ROTATE_V) || (surfaceType == ROTATE_P))
{
float axis = 0.25f;
float radius = 0.4f;
adorn->cylinderAlongX(radius, axis, controllerColor);
}
}
void Draw::selectionBox(
const Part& part,
Adorn* adorn,
const G3D::Color4& selectColor,
float lineThickness)
{
if ( adorn->getCamera() != NULL )
{
// determine distance from camera to object
const Vector3 camera_pos = adorn->getCamera()->getCameraCoordinateFrame().translation;
const Vector3 delta = part.coordinateFrame.translation - camera_pos;
const float delta_squared = delta.squaredLength();
// determine size of object by using longest axis
const float grid_size = part.gridSize[part.gridSize.primaryAxis()];
const float grid_size_squared = grid_size * grid_size;
// make sure the distance is greater than the size
if ( delta_squared > grid_size_squared )
{
// adjust distance based on thickness of lines
const float threshold_dist = SelectionBoxLineThreshold * lineThickness;
const float threshold_dist_squared = threshold_dist * threshold_dist;
// subtract the size from the distance and compare to the threshold
if ( delta_squared - grid_size_squared > threshold_dist_squared )
{
// switch to drawing lines
const Vector3 half_size = part.gridSize / 2;
adorn->setObjectToWorldMatrix(part.coordinateFrame);
DrawAdorn::outlineBox(adorn,Extents(-half_size,half_size),selectColor);
return;
}
}
}
adorn->setObjectToWorldMatrix( part.coordinateFrame );
Vector3 halfSize = 0.5f * part.gridSize;
float highlight = lineThickness;
for (int c1 = 0; c1 < 3; ++c1) { // x, y, z
int c2 = (c1 + 1) % 3; // y
int c3 = (c1 + 2) % 3; // z
for (int d2 = -1; d2 < 2; d2+=2) {
for (int d3 = -1; d3 < 2; d3+=2) {
// for c1, shorten the length of the edge segment by highlight size
// on both ends so that the edge segments do not overlap on the
// corners
Vector3 p0, p1;
p0[c1] = -halfSize[c1] - (c1 == 0 ? highlight : -highlight);
p0[c2] = d2 * halfSize[c2] - highlight;
p0[c3] = d3 * halfSize[c3] - highlight;
p1[c1] = halfSize[c1] + (c1 == 0 ? highlight : -highlight);
p1[c2] = d2 * halfSize[c2] + highlight;
p1[c3] = d3 * halfSize[c3] + highlight;
if (p0.x <= p1.x && p0.y <= p1.y && p0.z <= p1.z)
adorn->box( AABox(p0, p1),
selectColor );
}
}
}
}
} // namespace