Merge branch 'master' of github.com:highfidelity/hifi into actions-over-wire

This commit is contained in:
Seth Alves 2015-06-18 12:45:40 -07:00
commit 99afd07395
16 changed files with 233 additions and 222 deletions

View file

@ -57,7 +57,7 @@ const float CHAT_MESSAGE_SCALE = 0.0015f;
const float CHAT_MESSAGE_HEIGHT = 0.1f; const float CHAT_MESSAGE_HEIGHT = 0.1f;
const float DISPLAYNAME_FADE_TIME = 0.5f; const float DISPLAYNAME_FADE_TIME = 0.5f;
const float DISPLAYNAME_FADE_FACTOR = pow(0.01f, 1.0f / DISPLAYNAME_FADE_TIME); const float DISPLAYNAME_FADE_FACTOR = pow(0.01f, 1.0f / DISPLAYNAME_FADE_TIME);
const float DISPLAYNAME_ALPHA = 0.95f; const float DISPLAYNAME_ALPHA = 1.0f;
const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f; const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f;
namespace render { namespace render {
@ -280,9 +280,9 @@ enum TextRendererType {
}; };
static TextRenderer3D* textRenderer(TextRendererType type) { static TextRenderer3D* textRenderer(TextRendererType type) {
static TextRenderer3D* chatRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, 24, -1, static TextRenderer3D* chatRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, -1,
false, TextRenderer3D::SHADOW_EFFECT); false, TextRenderer3D::SHADOW_EFFECT);
static TextRenderer3D* displayNameRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, 12); static TextRenderer3D* displayNameRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY);
switch(type) { switch(type) {
case CHAT: case CHAT:
@ -323,7 +323,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
_referential->update(); _referential->update();
} }
auto batch = renderArgs->_batch; auto& batch = *renderArgs->_batch;
if (postLighting && if (postLighting &&
glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(), _position) < 10.0f) { glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(), _position) < 10.0f) {
@ -354,9 +354,9 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
Transform pointerTransform; Transform pointerTransform;
pointerTransform.setTranslation(position); pointerTransform.setTranslation(position);
pointerTransform.setRotation(rotation); pointerTransform.setRotation(rotation);
batch->setModelTransform(pointerTransform); batch.setModelTransform(pointerTransform);
deferredLighting->bindSimpleProgram(*batch); deferredLighting->bindSimpleProgram(batch);
geometryCache->renderLine(*batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor); geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor);
} }
} }
@ -377,9 +377,9 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
Transform pointerTransform; Transform pointerTransform;
pointerTransform.setTranslation(position); pointerTransform.setTranslation(position);
pointerTransform.setRotation(rotation); pointerTransform.setRotation(rotation);
batch->setModelTransform(pointerTransform); batch.setModelTransform(pointerTransform);
deferredLighting->bindSimpleProgram(*batch); deferredLighting->bindSimpleProgram(batch);
geometryCache->renderLine(*batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor); geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor);
} }
} }
} }
@ -464,8 +464,8 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
} }
Transform transform; Transform transform;
transform.setTranslation(position); transform.setTranslation(position);
batch->setModelTransform(transform); batch.setModelTransform(transform);
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(*batch, LOOK_AT_INDICATOR_RADIUS DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, LOOK_AT_INDICATOR_RADIUS
, 15, 15, LOOK_AT_INDICATOR_COLOR); , 15, 15, LOOK_AT_INDICATOR_COLOR);
} }
} }
@ -492,14 +492,14 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
Transform transform; Transform transform;
transform.setTranslation(_position); transform.setTranslation(_position);
transform.setScale(height); transform.setScale(height);
batch->setModelTransform(transform); batch.setModelTransform(transform);
if (_voiceSphereID == GeometryCache::UNKNOWN_ID) { if (_voiceSphereID == GeometryCache::UNKNOWN_ID) {
_voiceSphereID = DependencyManager::get<GeometryCache>()->allocateID(); _voiceSphereID = DependencyManager::get<GeometryCache>()->allocateID();
} }
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(*batch); DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch);
DependencyManager::get<GeometryCache>()->renderSphere(*batch, sphereRadius, 15, 15, DependencyManager::get<GeometryCache>()->renderSphere(batch, sphereRadius, 15, 15,
glm::vec4(SPHERE_COLOR[0], SPHERE_COLOR[1], SPHERE_COLOR[2], 1.0f - angle / MAX_SPHERE_ANGLE), true, glm::vec4(SPHERE_COLOR[0], SPHERE_COLOR[1], SPHERE_COLOR[2], 1.0f - angle / MAX_SPHERE_ANGLE), true,
_voiceSphereID); _voiceSphereID);
} }
@ -507,14 +507,12 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
} }
const float DISPLAYNAME_DISTANCE = 20.0f; const float DISPLAYNAME_DISTANCE = 20.0f;
setShowDisplayName(renderArgs->_renderMode == RenderArgs::NORMAL_RENDER_MODE && distanceToTarget < DISPLAYNAME_DISTANCE); setShowDisplayName(distanceToTarget < DISPLAYNAME_DISTANCE);
if (renderArgs->_renderMode != RenderArgs::NORMAL_RENDER_MODE || (isMyAvatar() && auto cameraMode = Application::getInstance()->getCamera()->getMode();
Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON)) { if (!isMyAvatar() || cameraMode != CAMERA_MODE_FIRST_PERSON) {
return; renderDisplayName(batch, *renderArgs->_viewFrustum);
} }
renderDisplayName(renderArgs);
} }
glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
@ -654,8 +652,8 @@ float Avatar::getBillboardSize() const {
return _scale * BILLBOARD_DISTANCE * tanf(glm::radians(BILLBOARD_FIELD_OF_VIEW / 2.0f)); return _scale * BILLBOARD_DISTANCE * tanf(glm::radians(BILLBOARD_FIELD_OF_VIEW / 2.0f));
} }
glm::vec3 Avatar::getDisplayNamePosition() { glm::vec3 Avatar::getDisplayNamePosition() const {
glm::vec3 namePosition; glm::vec3 namePosition(0.0f);
if (getSkeletonModel().getNeckPosition(namePosition)) { if (getSkeletonModel().getNeckPosition(namePosition)) {
namePosition += getBodyUpDirection() * getHeadHeight() * 1.1f; namePosition += getBodyUpDirection() * getHeadHeight() * 1.1f;
} else { } else {
@ -665,81 +663,72 @@ glm::vec3 Avatar::getDisplayNamePosition() {
return namePosition; return namePosition;
} }
float Avatar::calculateDisplayNameScaleFactor(const glm::vec3& textPosition, bool inHMD) { Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, float fontSize) const {
Transform result;
// We need to compute the scale factor such as the text remains with fixed size respect to window coordinates // We assume textPosition is whithin the frustum
// We project a unit vector and check the difference in screen coordinates, to check which is the glm::vec3 textPosition = getDisplayNamePosition();
// correction scale needed
// save the matrices for later scale correction factor // Compute viewProjection matrix
// The up vector must be relative to the rotation current rotation matrix: glm::mat4 projMat, viewMat;
// we set the identity Transform view;
frustum.evalProjectionMatrix(projMat);
frustum.evalViewTransform(view);
glm::mat4 viewProj = projMat * view.getInverseMatrix(viewMat);
// Used to determine correct scale
glm::vec3 testPoint0 = textPosition; glm::vec3 testPoint0 = textPosition;
glm::vec3 testPoint1 = textPosition + (Application::getInstance()->getCamera()->getRotation() * IDENTITY_UP); glm::vec3 testPoint1 = testPoint0 + glm::normalize(frustum.getUp());
// testPoints projections
double textWindowHeight; glm::vec4 p0 = viewProj * glm::vec4(testPoint0, 1.0);
glm::vec4 p1 = viewProj * glm::vec4(testPoint1, 1.0);
// TODO REMOVE vvv
GLint viewportMatrix[4]; GLint viewportMatrix[4];
glGetIntegerv(GL_VIEWPORT, viewportMatrix); glGetIntegerv(GL_VIEWPORT, viewportMatrix);
glm::dmat4 modelViewMatrix; glm::dmat4 modelViewMatrix;
float windowSizeX = viewportMatrix[2] - viewportMatrix[0];
float windowSizeY = viewportMatrix[3] - viewportMatrix[1]; float windowSizeY = viewportMatrix[3] - viewportMatrix[1];
// TODO REMOVE ^^^
glm::dmat4 projectionMatrix;
Application::getInstance()->getModelViewMatrix(&modelViewMatrix); const float DESIRED_HIGHT_ON_SCREEN = 20; // In pixels (this is double on retinas)
Application::getInstance()->getProjectionMatrix(&projectionMatrix);
// Projected point are between -1.0f and 1.0f, hence 0.5f * windowSizeY
double pixelHeight = 0.5f * windowSizeY * glm::abs((p1.y / p1.w) - (p0.y / p0.w)); //
glm::dvec4 p0 = modelViewMatrix * glm::dvec4(testPoint0, 1.0); // Handles pixel density (especially for macs retina displays)
p0 = projectionMatrix * p0; double devicePixelRatio = qApp->getDevicePixelRatio() * qApp->getRenderResolutionScale(); // pixels / unit
glm::dvec2 result0 = glm::vec2(windowSizeX * (p0.x / p0.w + 1.0f) * 0.5f, windowSizeY * (p0.y / p0.w + 1.0f) * 0.5f);
// Compute correct scale to apply
glm::dvec4 p1 = modelViewMatrix * glm::dvec4(testPoint1, 1.0); float scale = DESIRED_HIGHT_ON_SCREEN / (fontSize * pixelHeight) * devicePixelRatio;
p1 = projectionMatrix * p1;
glm::vec2 result1 = glm::vec2(windowSizeX * (p1.x / p1.w + 1.0f) * 0.5f, windowSizeY * (p1.y / p1.w + 1.0f) * 0.5f); // Compute pixel alignment offset
textWindowHeight = abs(result1.y - result0.y); float clipToPix = 0.5f * windowSizeY / p1.w; // Got from clip to pixel coordinates
glm::vec4 screenPos = clipToPix * p1; // in pixels coords
// need to scale to compensate for the font resolution due to the device glm::vec4 screenOffset = (glm::round(screenPos) - screenPos) / clipToPix; // in clip coords
float scaleFactor = QApplication::desktop()->windowHandle()->devicePixelRatio() * glm::vec3 worldOffset = glm::vec3(screenOffset.x, screenOffset.y, 0.0f) / (float)pixelHeight;
((textWindowHeight > EPSILON) ? 1.0f / textWindowHeight : 1.0f);
if (inHMD) { // Compute orientation
const float HMDMODE_NAME_SCALE = 0.65f; glm::vec3 eulerAngles = ::safeEulerAngles(frustum.getOrientation());
scaleFactor *= HMDMODE_NAME_SCALE; eulerAngles.z = 0.0f; // Cancel roll
} else { glm::quat orientation(eulerAngles); // back to quaternions
scaleFactor *= Application::getInstance()->getRenderResolutionScale();
} // Set transform (The order IS important)
return scaleFactor; result.setTranslation(textPosition);
result.setRotation(orientation); // Always face the screen
result.postTranslate(worldOffset); // Pixel alignment
result.setScale(scale);
return result;
} }
void Avatar::renderDisplayName(RenderArgs* renderArgs) { void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum) const {
auto batch = renderArgs->_batch;
bool shouldShowReceiveStats = DependencyManager::get<AvatarManager>()->shouldShowReceiveStats() && !isMyAvatar(); bool shouldShowReceiveStats = DependencyManager::get<AvatarManager>()->shouldShowReceiveStats() && !isMyAvatar();
// If we have nothing to draw, or it's tottaly transparent, return
if ((_displayName.isEmpty() && !shouldShowReceiveStats) || _displayNameAlpha == 0.0f) { if ((_displayName.isEmpty() && !shouldShowReceiveStats) || _displayNameAlpha == 0.0f) {
return; return;
} }
auto renderer = textRenderer(DISPLAYNAME);
// which viewing mode?
bool inHMD = Application::getInstance()->isHMDMode();
glm::vec3 textPosition = getDisplayNamePosition();
// we need "always facing camera": we must remove the camera rotation from the stac
glm::quat rotation = Application::getInstance()->getCamera()->getRotation();
// TODO: Fix scaling - at some point this or the text rendering changed in scale.
float scaleFactor = calculateDisplayNameScaleFactor(textPosition, inHMD);
scaleFactor /= 3.5f;
Transform textTransform;
textTransform.setTranslation(textPosition);
textTransform.setRotation(rotation);
textTransform.setScale(scaleFactor);
// optionally render timing stats for this avatar with the display name // optionally render timing stats for this avatar with the display name
QString renderedDisplayName = _displayName; QString renderedDisplayName = _displayName;
QRect nameDynamicRect = _displayNameBoundingRect;
if (shouldShowReceiveStats) { if (shouldShowReceiveStats) {
float kilobitsPerSecond = getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT; float kilobitsPerSecond = getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT;
@ -747,42 +736,43 @@ void Avatar::renderDisplayName(RenderArgs* renderArgs) {
if (!renderedDisplayName.isEmpty()) { if (!renderedDisplayName.isEmpty()) {
statsFormat.prepend(" - "); statsFormat.prepend(" - ");
} }
renderedDisplayName += statsFormat.arg(QString::number(kilobitsPerSecond, 'f', 2)).arg(getReceiveRate());
QString statsText = statsFormat.arg(QString::number(kilobitsPerSecond, 'f', 2)).arg(getReceiveRate());
renderedDisplayName += statsText;
glm::vec2 extent = textRenderer(DISPLAYNAME)->computeExtent(renderedDisplayName);
nameDynamicRect = QRect(0, 0, (int)extent.x, (int)extent.y);
} }
int text_x = -nameDynamicRect.width() / 2;
int text_y = -nameDynamicRect.height() / 2;
// draw a gray background
int left = text_x;
int right = left + nameDynamicRect.width();
int bottom = text_y;
int top = bottom + nameDynamicRect.height();
const int border = 8;
bottom -= border;
left -= border;
top += border;
right += border;
// Compute display name extent/position offset
glm::vec2 extent = renderer->computeExtent(renderedDisplayName);
QRect nameDynamicRect = QRect(0, 0, (int)extent.x, (int)extent.y);
const int text_x = -nameDynamicRect.width() / 2;
const int text_y = -nameDynamicRect.height() / 2;
// Compute background position/size
static const float SLIGHTLY_BEHIND = -0.05f;
const int border = 0.1f * nameDynamicRect.height();
const int left = text_x - border;
const int bottom = text_y - border;
const int width = nameDynamicRect.width() + 2.0f * border;
const int height = nameDynamicRect.height() + 2.0f * border;
const int bevelDistance = 0.1f * height;
// Display name and background colors
glm::vec4 textColor(0.93f, 0.93f, 0.93f, _displayNameAlpha); glm::vec4 textColor(0.93f, 0.93f, 0.93f, _displayNameAlpha);
glm::vec4 backgroundColor(0.2f, 0.2f, 0.2f, glm::vec4 backgroundColor(0.2f, 0.2f, 0.2f,
_displayNameAlpha * DISPLAYNAME_BACKGROUND_ALPHA / DISPLAYNAME_ALPHA); (_displayNameAlpha / DISPLAYNAME_ALPHA) * DISPLAYNAME_BACKGROUND_ALPHA);
// Compute display name transform
auto textTransform = calculateDisplayNameTransform(frustum, renderer->getFontSize());
// Render background slightly behind to avoid z-fighting
auto backgroundTransform = textTransform; auto backgroundTransform = textTransform;
backgroundTransform.postTranslate(glm::vec3(0.0f, 0.0f, -0.001f)); backgroundTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_BEHIND));
batch->setModelTransform(backgroundTransform); batch.setModelTransform(backgroundTransform);
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(*batch); DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch);
DependencyManager::get<GeometryCache>()->renderBevelCornersRect(*batch, left, bottom, right - left, top - bottom, 3, DependencyManager::get<GeometryCache>()->renderBevelCornersRect(batch, left, bottom, width, height,
backgroundColor); bevelDistance, backgroundColor);
// Render actual name
QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit(); QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit();
batch.setModelTransform(textTransform);
batch->setModelTransform(textTransform); renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor);
textRenderer(DISPLAYNAME)->draw(*batch, text_x, -text_y, nameUTF8.data(), textColor);
} }
bool Avatar::findRayIntersection(RayIntersectionInfo& intersection) const { bool Avatar::findRayIntersection(RayIntersectionInfo& intersection) const {
@ -987,13 +977,6 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
} }
} }
void Avatar::setDisplayName(const QString& displayName) {
AvatarData::setDisplayName(displayName);
// FIXME is this a sufficient replacement for tightBoundingRect?
glm::vec2 extent = textRenderer(DISPLAYNAME)->computeExtent(displayName);
_displayNameBoundingRect = QRect(0, 0, (int)extent.x, (int)extent.y);
}
void Avatar::setBillboard(const QByteArray& billboard) { void Avatar::setBillboard(const QByteArray& billboard) {
AvatarData::setBillboard(billboard); AvatarData::setBillboard(billboard);
@ -1092,7 +1075,12 @@ float Avatar::getSkeletonHeight() const {
float Avatar::getHeadHeight() const { float Avatar::getHeadHeight() const {
Extents extents = getHead()->getFaceModel().getMeshExtents(); Extents extents = getHead()->getFaceModel().getMeshExtents();
if (!extents.isEmpty() && extents.isValid()) { if (!extents.isEmpty() && extents.isValid()) {
return extents.maximum.y - extents.minimum.y;
// HACK: We have a really odd case when fading out for some models where this value explodes
float result = extents.maximum.y - extents.minimum.y;
if (result >= 0.0f && result < 100.0f * _scale ) {
return result;
}
} }
extents = _skeletonModel.getMeshExtents(); extents = _skeletonModel.getMeshExtents();
@ -1116,7 +1104,7 @@ void Avatar::setShowDisplayName(bool showDisplayName) {
} }
// For myAvatar, the alpha update is not done (called in simulate for other avatars) // For myAvatar, the alpha update is not done (called in simulate for other avatars)
if (DependencyManager::get<AvatarManager>()->getMyAvatar() == this) { if (isMyAvatar()) {
if (showDisplayName) { if (showDisplayName) {
_displayNameAlpha = DISPLAYNAME_ALPHA; _displayNameAlpha = DISPLAYNAME_ALPHA;
} else { } else {
@ -1129,7 +1117,6 @@ void Avatar::setShowDisplayName(bool showDisplayName) {
} else { } else {
_displayNameTargetAlpha = 0.0f; _displayNameTargetAlpha = 0.0f;
} }
} }
// virtual // virtual

View file

@ -98,6 +98,7 @@ public:
//getters //getters
bool isInitialized() const { return _initialized; } bool isInitialized() const { return _initialized; }
SkeletonModel& getSkeletonModel() { return _skeletonModel; } SkeletonModel& getSkeletonModel() { return _skeletonModel; }
const SkeletonModel& getSkeletonModel() const { return _skeletonModel; }
const QVector<Model*>& getAttachmentModels() const { return _attachmentModels; } const QVector<Model*>& getAttachmentModels() const { return _attachmentModels; }
glm::vec3 getChestPosition() const; glm::vec3 getChestPosition() const;
float getScale() const { return _scale; } float getScale() const { return _scale; }
@ -131,7 +132,7 @@ public:
/// \return whether or not the plane penetrated /// \return whether or not the plane penetrated
bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions); bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions);
virtual bool isMyAvatar() { return false; } virtual bool isMyAvatar() const { return false; }
virtual QVector<glm::quat> getJointRotations() const; virtual QVector<glm::quat> getJointRotations() const;
virtual glm::quat getJointRotation(int index) const; virtual glm::quat getJointRotation(int index) const;
@ -141,7 +142,6 @@ public:
virtual void setFaceModelURL(const QUrl& faceModelURL); virtual void setFaceModelURL(const QUrl& faceModelURL);
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData); virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData);
virtual void setDisplayName(const QString& displayName);
virtual void setBillboard(const QByteArray& billboard); virtual void setBillboard(const QByteArray& billboard);
void setShowDisplayName(bool showDisplayName); void setShowDisplayName(bool showDisplayName);
@ -232,10 +232,10 @@ protected:
float getSkeletonHeight() const; float getSkeletonHeight() const;
float getHeadHeight() const; float getHeadHeight() const;
float getPelvisFloatingHeight() const; float getPelvisFloatingHeight() const;
glm::vec3 getDisplayNamePosition(); glm::vec3 getDisplayNamePosition() const;
float calculateDisplayNameScaleFactor(const glm::vec3& textPosition, bool inHMD); Transform calculateDisplayNameTransform(const ViewFrustum& frustum, float fontSize) const;
void renderDisplayName(RenderArgs* renderArgs); void renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum) const;
virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel = 0.0f); virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel = 0.0f);
virtual bool shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& cameraPosition) const; virtual bool shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& cameraPosition) const;
virtual void fixupModelsInScene(); virtual void fixupModelsInScene();

View file

@ -90,7 +90,7 @@ public:
void relayDriveKeysToCharacterController(); void relayDriveKeysToCharacterController();
bool isMyAvatar() { return true; } bool isMyAvatar() const { return true; }
bool isLookingAtLeftEye(); bool isLookingAtLeftEye();

View file

@ -52,9 +52,8 @@ AvatarData::AvatarData() :
_headData(NULL), _headData(NULL),
_handData(NULL), _handData(NULL),
_faceModelURL("http://invalid.com"), _faceModelURL("http://invalid.com"),
_displayNameBoundingRect(), _displayNameTargetAlpha(1.0f),
_displayNameTargetAlpha(0.0f), _displayNameAlpha(1.0f),
_displayNameAlpha(0.0f),
_billboard(), _billboard(),
_errorLogExpiry(0), _errorLogExpiry(0),
_owningAvatarMixer(), _owningAvatarMixer(),

View file

@ -163,7 +163,7 @@ public:
AvatarData(); AvatarData();
virtual ~AvatarData(); virtual ~AvatarData();
virtual bool isMyAvatar() { return false; } virtual bool isMyAvatar() const { return false; }
const QUuid& getSessionUUID() const { return _sessionUUID; } const QUuid& getSessionUUID() const { return _sessionUUID; }
@ -378,7 +378,6 @@ protected:
QVector<AttachmentData> _attachmentData; QVector<AttachmentData> _attachmentData;
QString _displayName; QString _displayName;
QRect _displayNameBoundingRect;
float _displayNameTargetAlpha; float _displayNameTargetAlpha;
float _displayNameAlpha; float _displayNameAlpha;

View file

@ -70,7 +70,7 @@ public:
typedef Stream::Slot Slot; typedef Stream::Slot Slot;
Batch(); Batch();
Batch(const Batch& batch); explicit Batch(const Batch& batch);
~Batch(); ~Batch();
void clear(); void clear();
@ -148,6 +148,7 @@ public:
void _glDrawBuffers(GLsizei n, const GLenum* bufs); void _glDrawBuffers(GLsizei n, const GLenum* bufs);
void _glUseProgram(GLuint program); void _glUseProgram(GLuint program);
void _glUniform1i(GLint location, GLint v0);
void _glUniform1f(GLint location, GLfloat v0); void _glUniform1f(GLint location, GLfloat v0);
void _glUniform2f(GLint location, GLfloat v0, GLfloat v1); void _glUniform2f(GLint location, GLfloat v0, GLfloat v1);
void _glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); void _glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
@ -210,6 +211,7 @@ public:
COMMAND_glDrawBuffers, COMMAND_glDrawBuffers,
COMMAND_glUseProgram, COMMAND_glUseProgram,
COMMAND_glUniform1i,
COMMAND_glUniform1f, COMMAND_glUniform1f,
COMMAND_glUniform2f, COMMAND_glUniform2f,
COMMAND_glUniform3f, COMMAND_glUniform3f,

View file

@ -59,6 +59,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
(&::gpu::GLBackend::do_glDrawBuffers), (&::gpu::GLBackend::do_glDrawBuffers),
(&::gpu::GLBackend::do_glUseProgram), (&::gpu::GLBackend::do_glUseProgram),
(&::gpu::GLBackend::do_glUniform1i),
(&::gpu::GLBackend::do_glUniform1f), (&::gpu::GLBackend::do_glUniform1f),
(&::gpu::GLBackend::do_glUniform2f), (&::gpu::GLBackend::do_glUniform2f),
(&::gpu::GLBackend::do_glUniform3f), (&::gpu::GLBackend::do_glUniform3f),
@ -433,6 +434,28 @@ void GLBackend::do_glUseProgram(Batch& batch, uint32 paramOffset) {
(void) CHECK_GL_ERROR(); (void) CHECK_GL_ERROR();
} }
void Batch::_glUniform1i(GLint location, GLint v0) {
if (location < 0) {
return;
}
ADD_COMMAND_GL(glUniform1i);
_params.push_back(v0);
_params.push_back(location);
DO_IT_NOW(_glUniform1i, 1);
}
void GLBackend::do_glUniform1i(Batch& batch, uint32 paramOffset) {
if (_pipeline._program == 0) {
// We should call updatePipeline() to bind the program but we are not doing that
// because these uniform setters are deprecated and we don;t want to create side effect
return;
}
glUniform1f(
batch._params[paramOffset + 1]._int,
batch._params[paramOffset + 0]._int);
(void) CHECK_GL_ERROR();
}
void Batch::_glUniform1f(GLint location, GLfloat v0) { void Batch::_glUniform1f(GLint location, GLfloat v0) {
if (location < 0) { if (location < 0) {
return; return;

View file

@ -377,6 +377,7 @@ protected:
void do_glDrawBuffers(Batch& batch, uint32 paramOffset); void do_glDrawBuffers(Batch& batch, uint32 paramOffset);
void do_glUseProgram(Batch& batch, uint32 paramOffset); void do_glUseProgram(Batch& batch, uint32 paramOffset);
void do_glUniform1i(Batch& batch, uint32 paramOffset);
void do_glUniform1f(Batch& batch, uint32 paramOffset); void do_glUniform1f(Batch& batch, uint32 paramOffset);
void do_glUniform2f(Batch& batch, uint32 paramOffset); void do_glUniform2f(Batch& batch, uint32 paramOffset);
void do_glUniform3f(Batch& batch, uint32 paramOffset); void do_glUniform3f(Batch& batch, uint32 paramOffset);

View file

@ -72,7 +72,7 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
thePipeline = gpu::PipelinePointer(gpu::Pipeline::create(skyShader, skyState)); thePipeline = gpu::PipelinePointer(gpu::Pipeline::create(skyShader, skyState));
const float CLIP = 1.0; const float CLIP = 1.0f;
const glm::vec2 vertices[4] = { {-CLIP, -CLIP}, {CLIP, -CLIP}, {-CLIP, CLIP}, {CLIP, CLIP}}; const glm::vec2 vertices[4] = { {-CLIP, -CLIP}, {CLIP, -CLIP}, {-CLIP, CLIP}, {CLIP, CLIP}};
theBuffer.reset(new gpu::Buffer(sizeof(vertices), (const gpu::Byte*) vertices)); theBuffer.reset(new gpu::Buffer(sizeof(vertices), (const gpu::Byte*) vertices));
@ -110,7 +110,7 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
} else { } else {
// skybox has no cubemap, just clear the color buffer // skybox has no cubemap, just clear the color buffer
auto color = skybox.getColor(); auto color = skybox.getColor();
batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(color, 0.0f), 0.f, 0); batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(color, 0.0f), 0.0f, 0);
} }
} }

View file

@ -545,9 +545,9 @@ void ViewFrustum::printDebugDetails() const {
glm::vec2 ViewFrustum::projectPoint(glm::vec3 point, bool& pointInView) const { glm::vec2 ViewFrustum::projectPoint(glm::vec3 point, bool& pointInView) const {
glm::vec4 pointVec4 = glm::vec4(point,1); glm::vec4 pointVec4 = glm::vec4(point, 1.0f);
glm::vec4 projectedPointVec4 = _ourModelViewProjectionMatrix * pointVec4; glm::vec4 projectedPointVec4 = _ourModelViewProjectionMatrix * pointVec4;
pointInView = (projectedPointVec4.w > 0); // math! If the w result is negative then the point is behind the viewer pointInView = (projectedPointVec4.w > 0.0f); // math! If the w result is negative then the point is behind the viewer
// what happens with w is 0??? // what happens with w is 0???
float x = projectedPointVec4.x / projectedPointVec4.w; float x = projectedPointVec4.x / projectedPointVec4.w;

View file

@ -1025,13 +1025,13 @@ void GeometryCache::renderBevelCornersRect(gpu::Batch& batch, int x, int y, int
#endif // def WANT_DEBUG #endif // def WANT_DEBUG
} }
const int FLOATS_PER_VERTEX = 2; // vertices
const int vertices = 8;
if (!details.isCreated) { if (!details.isCreated) {
static const int FLOATS_PER_VERTEX = 2; // vertices
static const int NUM_VERTICES = 8;
static const int NUM_FLOATS = NUM_VERTICES * FLOATS_PER_VERTEX;
details.isCreated = true; details.isCreated = true;
details.vertices = vertices; details.vertices = NUM_VERTICES;
details.vertexSize = FLOATS_PER_VERTEX; details.vertexSize = FLOATS_PER_VERTEX;
gpu::BufferPointer verticesBuffer(new gpu::Buffer()); gpu::BufferPointer verticesBuffer(new gpu::Buffer());
@ -1044,62 +1044,65 @@ void GeometryCache::renderBevelCornersRect(gpu::Batch& batch, int x, int y, int
details.streamFormat = streamFormat; details.streamFormat = streamFormat;
details.stream = stream; details.stream = stream;
details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ), 0); details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ));
details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA)); details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA));
details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride);
details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride);
int vertexPoints = vertices * FLOATS_PER_VERTEX; GLfloat vertexBuffer[NUM_FLOATS]; // only vertices, no normals because we're a 2D quad
GLfloat* vertexBuffer = new GLfloat[vertexPoints]; // only vertices, no normals because we're a 2D quad
GLfloat* vertex = vertexBuffer;
int vertexPoint = 0; int vertexPoint = 0;
// left side // Triangle strip points
vertex[vertexPoint++] = x; // 3 ------ 5
vertex[vertexPoint++] = y + bevelDistance; // / \
// 1 7
// | |
// 2 8
// \ /
// 4 ------ 6
vertex[vertexPoint++] = x; // 1
vertex[vertexPoint++] = y + height - bevelDistance; vertexBuffer[vertexPoint++] = x;
vertexBuffer[vertexPoint++] = y + height - bevelDistance;
// top side // 2
vertex[vertexPoint++] = x + bevelDistance; vertexBuffer[vertexPoint++] = x;
vertex[vertexPoint++] = y + height; vertexBuffer[vertexPoint++] = y + bevelDistance;
// 3
vertexBuffer[vertexPoint++] = x + bevelDistance;
vertexBuffer[vertexPoint++] = y + height;
// 4
vertexBuffer[vertexPoint++] = x + bevelDistance;
vertexBuffer[vertexPoint++] = y;
// 5
vertexBuffer[vertexPoint++] = x + width - bevelDistance;
vertexBuffer[vertexPoint++] = y + height;
// 6
vertexBuffer[vertexPoint++] = x + width - bevelDistance;
vertexBuffer[vertexPoint++] = y;
// 7
vertexBuffer[vertexPoint++] = x + width;
vertexBuffer[vertexPoint++] = y + height - bevelDistance;
// 8
vertexBuffer[vertexPoint++] = x + width;
vertexBuffer[vertexPoint++] = y + bevelDistance;
vertex[vertexPoint++] = x + width - bevelDistance;
vertex[vertexPoint++] = y + height;
// right
vertex[vertexPoint++] = x + width;
vertex[vertexPoint++] = y + height - bevelDistance;
vertex[vertexPoint++] = x + width;
vertex[vertexPoint++] = y + bevelDistance;
// bottom
vertex[vertexPoint++] = x + width - bevelDistance;
vertex[vertexPoint++] = y;
vertex[vertexPoint++] = x +bevelDistance;
vertex[vertexPoint++] = y;
const int NUM_COLOR_SCALARS_PER_QUAD = 8;
int compactColor = ((int(color.x * 255.0f) & 0xFF)) | int compactColor = ((int(color.x * 255.0f) & 0xFF)) |
((int(color.y * 255.0f) & 0xFF) << 8) | ((int(color.y * 255.0f) & 0xFF) << 8) |
((int(color.z * 255.0f) & 0xFF) << 16) | ((int(color.z * 255.0f) & 0xFF) << 16) |
((int(color.w * 255.0f) & 0xFF) << 24); ((int(color.w * 255.0f) & 0xFF) << 24);
int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor, int colors[NUM_VERTICES] = { compactColor, compactColor, compactColor, compactColor,
compactColor, compactColor, compactColor, compactColor }; compactColor, compactColor, compactColor, compactColor };
details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer);
details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors);
delete[] vertexBuffer;
} }
batch.setInputFormat(details.streamFormat); batch.setInputFormat(details.streamFormat);
batch.setInputStream(0, *details.stream); batch.setInputStream(0, *details.stream);
batch.draw(gpu::QUADS, 4, 0); batch.draw(gpu::TRIANGLE_STRIP, details.vertices, 0);
} }
void GeometryCache::renderQuad(const glm::vec2& minCorner, const glm::vec2& maxCorner, const glm::vec4& color, int id) { void GeometryCache::renderQuad(const glm::vec2& minCorner, const glm::vec2& maxCorner, const glm::vec4& color, int id) {

View file

@ -30,11 +30,6 @@
#include "GeometryCache.h" #include "GeometryCache.h"
#include "DeferredLightingEffect.h" #include "DeferredLightingEffect.h"
// FIXME support the shadow effect, or remove it from the API
// FIXME figure out how to improve the anti-aliasing on the
// interior of the outline fonts
const float DEFAULT_POINT_SIZE = 12;
// Helper functions for reading binary data from an IO device // Helper functions for reading binary data from an IO device
template<class T> template<class T>
void readStream(QIODevice& in, T& t) { void readStream(QIODevice& in, T& t) {
@ -117,7 +112,7 @@ public:
// Render string to batch // Render string to batch
void drawString(gpu::Batch& batch, float x, float y, const QString& str, void drawString(gpu::Batch& batch, float x, float y, const QString& str,
const glm::vec4& color, TextRenderer3D::EffectType effectType, const glm::vec4* color, TextRenderer3D::EffectType effectType,
const glm::vec2& bound); const glm::vec2& bound);
private: private:
@ -367,7 +362,7 @@ void Font3D::setupGPU() {
} }
} }
void Font3D::drawString(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color, void Font3D::drawString(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4* color,
TextRenderer3D::EffectType effectType, const glm::vec2& bounds) { TextRenderer3D::EffectType effectType, const glm::vec2& bounds) {
if (str == "") { if (str == "") {
return; return;
@ -429,39 +424,33 @@ void Font3D::drawString(gpu::Batch& batch, float x, float y, const QString& str,
setupGPU(); setupGPU();
batch.setPipeline(_pipeline); batch.setPipeline(_pipeline);
batch.setUniformTexture(_fontLoc, _texture); batch.setUniformTexture(_fontLoc, _texture);
batch._glUniform1f(_outlineLoc, (effectType == TextRenderer3D::OUTLINE_EFFECT) ? 1.0f : 0.0f); batch._glUniform1i(_outlineLoc, (effectType == TextRenderer3D::OUTLINE_EFFECT));
batch._glUniform4fv(_colorLoc, 1, (const GLfloat*)&color); batch._glUniform4fv(_colorLoc, 1, (const GLfloat*)color);
batch.setInputFormat(_format); batch.setInputFormat(_format);
batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride); batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride);
batch.draw(gpu::QUADS, _numVertices, 0); batch.draw(gpu::QUADS, _numVertices, 0);
} }
TextRenderer3D* TextRenderer3D::getInstance(const char* family, float pointSize, TextRenderer3D* TextRenderer3D::getInstance(const char* family,
int weight, bool italic, EffectType effect, int effectThickness, int weight, bool italic, EffectType effect, int effectThickness) {
const QColor& color) { return new TextRenderer3D(family, weight, italic, effect, effectThickness);
if (pointSize < 0) {
pointSize = DEFAULT_POINT_SIZE;
}
return new TextRenderer3D(family, pointSize, weight, italic, effect,
effectThickness, color);
} }
TextRenderer3D::TextRenderer3D(const char* family, float pointSize, int weight, bool italic, TextRenderer3D::TextRenderer3D(const char* family, int weight, bool italic,
EffectType effect, int effectThickness, const QColor& color) : EffectType effect, int effectThickness) :
_effectType(effect), _effectType(effect),
_effectThickness(effectThickness), _effectThickness(effectThickness),
_color(toGlm(color)),
_font(loadFont3D(family)) { _font(loadFont3D(family)) {
if (!_font) { if (!_font) {
qWarning() << "Unable to load font with family " << family; qWarning() << "Unable to load font with family " << family;
_font = loadFont3D("Courier"); _font = loadFont3D("Courier");
} }
if (1 != _effectThickness) { if (1 != _effectThickness) {
qWarning() << "Effect thickness not current supported"; qWarning() << "Effect thickness not currently supported";
} }
if (NO_EFFECT != _effectType && OUTLINE_EFFECT != _effectType) { if (NO_EFFECT != _effectType && OUTLINE_EFFECT != _effectType) {
qWarning() << "Effect thickness not current supported"; qWarning() << "Effect type not currently supported";
} }
} }
@ -486,11 +475,9 @@ void TextRenderer3D::draw(gpu::Batch& batch, float x, float y, const QString& st
const glm::vec2& bounds) { const glm::vec2& bounds) {
// The font does all the OpenGL work // The font does all the OpenGL work
if (_font) { if (_font) {
glm::vec4 actualColor(color); // Cache color so that the pointer stays valid.
if (actualColor.r < 0) { _color = color;
actualColor = _color; _font->drawString(batch, x, y, str, &_color, _effectType, bounds);
}
_font->drawString(batch, x, y, str, actualColor, _effectType, bounds);
} }
} }

View file

@ -39,25 +39,24 @@ class Batch;
class Font3D; class Font3D;
// TextRenderer3D is actually a fairly thin wrapper around a Font class // TextRenderer3D is actually a fairly thin wrapper around a Font class
// defined in the cpp file. // defined in the cpp file.
class TextRenderer3D { class TextRenderer3D {
public: public:
enum EffectType { NO_EFFECT, SHADOW_EFFECT, OUTLINE_EFFECT }; enum EffectType { NO_EFFECT, SHADOW_EFFECT, OUTLINE_EFFECT };
static TextRenderer3D* getInstance(const char* family, float pointSize = -1, int weight = -1, bool italic = false, static TextRenderer3D* getInstance(const char* family, int weight = -1, bool italic = false,
EffectType effect = NO_EFFECT, int effectThickness = 1, const QColor& color = QColor(255, 255, 255)); EffectType effect = NO_EFFECT, int effectThickness = 1);
~TextRenderer3D(); ~TextRenderer3D();
glm::vec2 computeExtent(const QString& str) const; glm::vec2 computeExtent(const QString& str) const;
float getFontSize() const; float getFontSize() const; // Pixel size
void draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color = glm::vec4(-1.0f), void draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color = glm::vec4(1.0f),
const glm::vec2& bounds = glm::vec2(-1.0f)); const glm::vec2& bounds = glm::vec2(-1.0f));
private: private:
TextRenderer3D(const char* family, float pointSize = -1, int weight = -1, bool italic = false, TextRenderer3D(const char* family, int weight = -1, bool italic = false,
EffectType effect = NO_EFFECT, int effectThickness = 1, const QColor& color = QColor(255, 255, 255)); EffectType effect = NO_EFFECT, int effectThickness = 1);
// the type of effect to apply // the type of effect to apply
const EffectType _effectType; const EffectType _effectType;
@ -66,7 +65,7 @@ private:
const int _effectThickness; const int _effectThickness;
// text color // text color
const glm::vec4 _color; glm::vec4 _color;
Font3D* _font; Font3D* _font;
}; };

View file

@ -11,18 +11,21 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
uniform sampler2D Font; uniform sampler2D Font;
uniform float Outline; uniform bool Outline;
uniform vec4 Color; uniform vec4 Color;
const float gamma = 2.6; // the interpolated normal
const float smoothing = 100.0; varying vec4 interpolatedNormal;
const float gamma = 2.2;
const float smoothing = 64.0;
const float interiorCutoff = 0.8; const float interiorCutoff = 0.8;
const float outlineExpansion = 0.2; const float outlineExpansion = 0.2;
void main() { void main() {
// retrieve signed distance // retrieve signed distance
float sdf = texture2D(Font, gl_TexCoord[0].xy).g; float sdf = texture2D(Font, gl_TexCoord[0].xy).g;
if (Outline == 1.0f) { if (Outline) {
if (sdf > interiorCutoff) { if (sdf > interiorCutoff) {
sdf = 1.0 - sdf; sdf = 1.0 - sdf;
} else { } else {
@ -31,7 +34,7 @@ void main() {
} }
// perform adaptive anti-aliasing of the edges // perform adaptive anti-aliasing of the edges
// The larger we're rendering, the less anti-aliasing we need // The larger we're rendering, the less anti-aliasing we need
float s = smoothing * length(fwidth(gl_TexCoord[0])); float s = smoothing * length(fwidth(gl_TexCoord[0].xy));
float w = clamp( s, 0.0, 0.5); float w = clamp( s, 0.0, 0.5);
float a = smoothstep(0.5 - w, 0.5 + w, sdf); float a = smoothstep(0.5 - w, 0.5 + w, sdf);
@ -43,5 +46,7 @@ void main() {
} }
// final color // final color
gl_FragColor = vec4(Color.rgb, a); gl_FragData[0] = vec4(Color.rgb, Color.a * a);
gl_FragData[1] = vec4(interpolatedNormal.xyz, 0.0) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0);
gl_FragData[2] = vec4(0.0);
} }

View file

@ -13,6 +13,9 @@
<$declareStandardTransform()$> <$declareStandardTransform()$>
// the interpolated normal
varying vec4 interpolatedNormal;
void main() { void main() {
gl_TexCoord[0] = gl_MultiTexCoord0; gl_TexCoord[0] = gl_MultiTexCoord0;
@ -20,4 +23,7 @@ void main() {
TransformCamera cam = getTransformCamera(); TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject(); TransformObject obj = getTransformObject();
<$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$> <$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$>
<$transformModelToEyeDir(cam, obj, gl_Normal, interpolatedNormal.xyz)$>
interpolatedNormal = vec4(normalize(interpolatedNormal.xyz), 0.0);
} }

View file

@ -29,7 +29,7 @@ inline bool isValidScale(glm::vec3 scale) {
} }
inline bool isValidScale(float scale) { inline bool isValidScale(float scale) {
bool result = scale != 0.0f; bool result = scale != 0.0f && !glm::isnan(scale) && !glm::isinf(scale);
assert(result); assert(result);
return result; return result;
} }
@ -323,7 +323,7 @@ inline void Transform::setScale(const Vec3& scale) {
} }
inline void Transform::postScale(float scale) { inline void Transform::postScale(float scale) {
if (isValidScale(scale) || scale == 1.0f) { if (!isValidScale(scale) || scale == 1.0f) {
return; return;
} }
if (isScaling()) { if (isScaling()) {