Created
May 13, 2015 18:58
-
-
Save adderly/48e570ba900765d5ce60 to your computer and use it in GitHub Desktop.
ImageRenderer localBox modification.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) 2014 Google Inc. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
#include <qmath.h> | |
#include "Actor.h" | |
#include "Body.h" | |
#include "Camera.h" | |
#include "Engine.h" | |
#include "ImageRenderer.h" | |
#include "renderer/Texture.h" | |
#include "TextureManager.h" | |
#include "renderer/RenderList.h" | |
#include "renderer/Renderer.h" | |
#include "utils/Util.h" | |
ImageRenderer::ImageRenderer(QQuickItem* parent) : Graphic(parent) { | |
setFlag(ItemHasContents, true); | |
mRenderNode.callback = this; | |
setCulled(true); | |
mTextures.resize(2); | |
} | |
ImageRenderer::~ImageRenderer() { | |
} | |
void ImageRenderer::setVisible(bool value) { | |
mVisible = value; | |
updateVisibility(); | |
emit visibleChanged(); | |
} | |
void ImageRenderer::setSizeScale(float value) { | |
mSizeScale = value; | |
mTransformDirty = true; | |
emit sizeScaleChanged(); | |
} | |
void ImageRenderer::setAspectRatio(float value) { | |
mAspectRatio = value; | |
mTransformDirty = true; | |
emit aspectRatioChanged(); | |
} | |
void ImageRenderer::setSource(const QString& value) { | |
bool sourceChanging = value != mSource; | |
mSource = value; | |
if (sourceChanging) { | |
mSourceDirty = true; | |
} | |
emit sourceChanged(); | |
} | |
void ImageRenderer::setCacheRenderParams(bool value) { | |
mCacheRenderParams = value; | |
emit cacheRenderParamsChanged(); | |
} | |
void ImageRenderer::setCullingControlsBodyActive(bool value) { | |
mCullingControlsBodyActive = value; | |
emit cullingControlsBodyActiveChanged(); | |
} | |
void ImageRenderer::setCustomLocalBox(QRectF lbox){ | |
mCustomLocalBox = lbox; | |
emit customLocalBoxChanged(); | |
} | |
void ImageRenderer::reloadTexture() { | |
mTextures[mCurrentTexture] = Engine::getInstance()->getTextureManager()->getTexture(mSource); | |
mTransformDirty = true; | |
update(); | |
} | |
void ImageRenderer::updateLocalBoundingBox() { | |
QRectF bounds; | |
float textureAspectRatio = 1.0f; | |
if (mTextures[mCurrentTexture]) { | |
QSizeF textureSize = QSizeF( mTextures[mCurrentTexture]->getWidth(), mTextures[mCurrentTexture]->getHeight()); | |
computeDestTextureSize(&textureSize); | |
textureAspectRatio = textureSize.height() / (float) textureSize.width(); | |
float scale = mSizeScale; | |
float aspectRatio = mAspectRatio; | |
float scaleX = scale; | |
float scaleY = scale * aspectRatio; | |
if(mCustomLocalBox.isNull()){ | |
bounds = QRectF(-scaleX / 2, -scaleY / 2, scaleX, scaleY); | |
}else{ | |
bounds = mCustomLocalBox; | |
textureAspectRatio = 1; | |
} | |
} | |
// qDebug() << mSource << " Bounds = " << bounds; | |
mLocalBoundingBox = bounds; | |
mTextureAspectRatio = textureAspectRatio; | |
} | |
void ImageRenderer::render(RenderNode*) { | |
Engine::getInstance()->getRenderer()->drawSprite( | |
mRenderTexture, | |
Vector2(mRenderPos.x(), mRenderPos.y()), | |
mRenderSize.width(), mRenderSize.height(), | |
Vector2(mRenderTextureSubRect.x(), mRenderTextureSubRect.y()), | |
mRenderTextureSubRect.width(), mRenderTextureSubRect.height(), | |
mRenderRotation, | |
mRenderOpacity); | |
} | |
void ImageRenderer::synchronizeForRendering(RenderList* renderList) { | |
// Update our texture. | |
if (mSourceDirty) { | |
mSourceDirty = false; | |
reloadTexture(); | |
} | |
if (mTransformDirty) { | |
mTransformDirty = false; | |
updateLocalBoundingBox(); | |
} | |
// Because most objects will be off screen, cull using the object's origin. This lets us | |
// check one point, instead of four. | |
RenderParameters renderParams; | |
if (!mCacheRenderParams || !mHasCachedRenderParams) { | |
getFlattenedRenderParameters(&renderParams); | |
if (mCacheRenderParams) { | |
mCachedRenderParams = renderParams; | |
mHasCachedRenderParams = true; | |
} | |
} else { | |
renderParams = mCachedRenderParams; | |
} | |
Camera* camera = Engine::getInstance()->getCamera(); | |
QPointF worldPos = renderParams.worldPosition; | |
const QRectF& cullBounds = camera->getWorldCullBounds(); | |
// The maximum extent of beyond the origin when this object is rotated is SQRT(2) / 2 times the | |
// longest dimension (ie. when the object is at 45 degree rotation). As an approximation, we'll | |
// just use the longest dimension since that is even longer. | |
float sizeX = mLocalBoundingBox.width(); | |
float sizeY = mLocalBoundingBox.height(); | |
float majorAxisLength = qMax(fabsf(sizeX), fabsf(sizeY * mTextureAspectRatio)); | |
bool isCulled = (worldPos.x() + majorAxisLength) < cullBounds.left() | |
|| (worldPos.x() - majorAxisLength) > cullBounds.right() | |
|| (worldPos.y() + majorAxisLength) < cullBounds.top() | |
|| (worldPos.y() - majorAxisLength) > cullBounds.bottom(); | |
// TODO: If this object has not been culled yet, we could perform a more precise test using the | |
// image's bounding box, but we would have to account for rotation and scaling. | |
if (isCulled) { | |
// Hide node. | |
setCulled(true); | |
} else { | |
// Transform node. | |
if (!QQuickItem::isVisible()) { | |
setCulled(false); | |
} | |
if (!mCulled && mVisible && parentItem()->isVisible() && opacity() > 0.0f) { | |
QPointF renderPos = worldPos; | |
// For non-square textures, anchor their bottoms to the square centered at the pivot, | |
// of the width of the texture. | |
// TODO: This is a legacy thing. When we fixed the editor to support non-square | |
// textures, we did it in a funny way. We should consider fixing these | |
// (difficult). | |
float offsetAmount = (1.0f - mTextureAspectRatio) * 0.5f * mSizeScale * mAspectRatio; | |
if (offsetAmount != 0.0f) { | |
// The offset we apply needs to be rotated from the y-axis into rotated space. | |
float angleCos = renderParams.rotation == 0.0f ? 1.0f : qCos(renderParams.rotation); | |
float angleSin = renderParams.rotation == 0.0f ? 0.0f : qSin(renderParams.rotation); | |
renderPos += QPointF(offsetAmount * -angleSin, offsetAmount * angleCos); | |
} | |
// Copy double buffered data. | |
mRenderPos = renderPos; | |
mRenderSize = QSizeF(sizeX * 0.5f, sizeY * 0.5f * mTextureAspectRatio); | |
mRenderRotation = renderParams.rotation; | |
mRenderOpacity = renderParams.opacity; | |
mRenderNode.zDepth = renderParams.zDepth; | |
mRenderTextureSubRect = QRectF(0.0f, 0.0f, 1.0f, 1.0f); | |
computeSourceTextureRect(&mRenderTextureSubRect); | |
renderList->addNode(&mRenderNode); | |
} | |
} | |
// Always update texture to avoid dangling pointers. Avoid excessive shared pointer copies | |
// though because the texture usually changes infrequently. | |
if (mTextures[mCurrentTexture] != mRenderTexture) { | |
mRenderTexture = mTextures[mCurrentTexture]; | |
} | |
} | |
void ImageRenderer::componentComplete() { | |
Graphic::componentComplete(); | |
checkBody(); | |
} | |
void ImageRenderer::checkBody() { | |
if (mBody) { | |
return; | |
} | |
mActor = Util::findParentOfType<Actor>(this); | |
if (mActor) { | |
mBody = mActor->getBody(); | |
} | |
} | |
void ImageRenderer::computeDestTextureSize(QSizeF*) const { | |
// Default implementation does nothing. | |
} | |
void ImageRenderer::computeSourceTextureRect(QRectF*) const { | |
// Default implementation does nothing. | |
} | |
void ImageRenderer::markSourceTextureRectDirty() { | |
mTransformDirty = true; | |
} | |
void ImageRenderer::setCulled(bool value) { | |
mCulled = value; | |
updateVisibility(); | |
} | |
void ImageRenderer::updateVisibility() { | |
// We hid QQuickItem::setVisible, but we can still explicitly access it. Use it to hide our | |
// node. | |
bool visible = !mCulled && mVisible; | |
if (visible != mWasVisible) { | |
mWasVisible = visible; | |
QQuickItem::setVisible(visible); | |
if (mCullingControlsBodyActive) { | |
checkBody(); | |
if (mBody) { | |
mBody->setActive(visible); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) 2014 Google Inc. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
#ifndef IMAGERENDERER_H | |
#define IMAGERENDERER_H | |
#include <memory> | |
#include "Graphic.h" | |
#include "renderer/RenderNode.h" | |
class Actor; | |
class Body; | |
class Texture; | |
class QSGTexture; | |
/** | |
* @ingroup Engine | |
* @ingroup QQuickItem | |
* @brief Graphic which displays a texture as an image. | |
* | |
* The texture is loaded and cached using TextureManager. This class also performs a crude scissor | |
* test to cull images from view, reducing the load on the geometry batch merger freeing up CPU | |
* time. | |
* TODO: add flexibility to render into the qml normal tree. AKA: All it in the Graphic class. | |
* FIXME: Fix the an weird issue with texture aspect ratio. | |
* FIXME: MESS with the rendering and positioning OMG. Nightmare, nah just a kind of bad dream. | |
*/ | |
class ImageRenderer : public Graphic, public RenderableInterface { | |
Q_OBJECT | |
/** | |
* @brief Override of QQuickItem's visible property to set visibility. | |
* @note This is done to preserve the convention of using @c visible to control visibility. | |
* Internally we set @c QQuickItem's @c visible property, and @c visible is not @c virtual, so | |
* we cannot override it in the C++ sense. Instead we hide it. | |
*/ | |
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged) | |
/** | |
* @brief Width of this image, in world coordinates. | |
*/ | |
Q_PROPERTY(float sizeScale READ getSizeScale WRITE setSizeScale NOTIFY sizeScaleChanged) | |
/** | |
* @brief Height of this image, as a ratio of the width. | |
*/ | |
Q_PROPERTY(float aspectRatio READ getAspectRatio WRITE setAspectRatio NOTIFY aspectRatioChanged) | |
/** | |
* @brief Path to the source texture. | |
*/ | |
Q_PROPERTY(QString source READ getSource WRITE setSource NOTIFY sourceChanged) | |
/** | |
* @brief Controls whether or not the position, rotation, opacity, and z-depth are cached on | |
* first render, and cached copies are used subsequently. | |
* | |
* This improves performance, but creates stale data if the Actor is moving or transforming. | |
* @see Graphic::RenderParameters | |
*/ | |
Q_PROPERTY(bool cacheRenderParams READ getCacheRenderParams WRITE setCacheRenderParams | |
NOTIFY cacheRenderParamsChanged) | |
/** | |
* @brief Controls whether or not the Actor's associated Body's Body#active property is linked | |
* to this image's visibility. | |
* | |
* If this property is @c true, when this image is culled or its #visible is set to @c false, | |
* the associated Body will be deactivated. The Body is found through the Actor#body property. | |
*/ | |
Q_PROPERTY(bool cullingControlsBodyActive READ getCullingControlsBodyActive | |
WRITE setCullingControlsBodyActive NOTIFY cullingControlsBodyActiveChanged) | |
/** | |
* @brief Custom boundingbox. | |
*/ | |
Q_PROPERTY(QRectF customLocalBox READ getCustomLocalBox WRITE setCustomLocalBox NOTIFY customLocalBoxChanged) | |
public: | |
/** | |
* @brief Construct an ImageRenderer. | |
* @param parent Parent item | |
*/ | |
explicit ImageRenderer(QQuickItem* parent = nullptr); | |
virtual ~ImageRenderer(); | |
/** | |
* @brief Returns #visible. | |
*/ | |
bool isVisible() const { return mVisible; } | |
/** | |
* @brief Sets #visible. | |
* @param value Boolean to set #visible to | |
*/ | |
void setVisible(bool value); | |
/** | |
* @brief Returns #sizeScale. | |
*/ | |
float getSizeScale() const { return mSizeScale; } | |
/** | |
* @brief Sets #sizeScale. | |
* @param value Float to set #sizeScale to | |
*/ | |
void setSizeScale(float value); | |
/** | |
* @brief Returns #aspectRatio. | |
*/ | |
float getAspectRatio() const { return mAspectRatio; } | |
/** | |
* @brief Sets #aspectRatio. | |
* @param value Float to set #aspectRatio to | |
*/ | |
void setAspectRatio(float value); | |
/** | |
* @brief Returns #source. | |
*/ | |
const QString& getSource() const { return mSource; } | |
/** | |
* @brief Sets #source. | |
* @param value @c QString to set #source to | |
*/ | |
void setSource(const QString& value); | |
/** | |
* @brief Returns #cacheRenderParams. | |
*/ | |
bool getCacheRenderParams() const { return mCacheRenderParams; } | |
/** | |
* @brief Sets #cacheRenderParams. | |
* @param value Boolean to set #cacheRenderParams to | |
*/ | |
void setCacheRenderParams(bool value); | |
/** | |
* @brief Returns #cullingControlsBodyActive. | |
*/ | |
bool getCullingControlsBodyActive() const { return mCullingControlsBodyActive; } | |
/** | |
* @brief Sets #cullingControlsBodyActive. | |
* @param value Boolean to set #cullingControlsBodyActive to | |
*/ | |
void setCullingControlsBodyActive(bool value); | |
/** | |
* @brief This is for external image positioning. | |
*/ | |
void setCustomLocalBox(QRectF lbox); | |
/** | |
* @brief Returns the #customLocalBox. | |
*/ | |
QRectF getCustomLocalBox() { return mCustomLocalBox; } | |
/** | |
* @brief Returns whether or not this image has been culled because it is off screen. | |
*/ | |
bool isCulled() const { return mCulled; } | |
signals: | |
/** | |
* @brief Emitted when #visible changes. | |
*/ | |
void visibleChanged(); | |
/** | |
* @brief Emitted when #sizeScale changes. | |
*/ | |
void sizeScaleChanged(); | |
/** | |
* @brief Emitted when #aspectRatio changes. | |
*/ | |
void aspectRatioChanged(); | |
/** | |
* @brief Emitted when #source changes. | |
*/ | |
void sourceChanged(); | |
/** | |
* @brief Emitted when #cacheRenderParams changes. | |
*/ | |
void cacheRenderParamsChanged(); | |
/** | |
* @brief Emitted when #cullingControlsBodyActive changes. | |
*/ | |
void cullingControlsBodyActiveChanged(); | |
/** | |
* @brief Emitted when #customLocalBox changes. | |
*/ | |
void customLocalBoxChanged(); | |
protected: | |
virtual void synchronizeForRendering(RenderList* renderList) override; | |
virtual void render(RenderNode *node) override; | |
/** | |
* @brief Post-initialization that checks for the existence of a parent Actor and the parent | |
* Actor's Body, caching the results for use in rendering. | |
*/ | |
virtual void componentComplete() override; | |
/** | |
* @brief Computes the size to treat the source Texture, for purposes of computing destination | |
* rectangle size. | |
* | |
* The actual texture size is passed in as @c textureSize. The desired texture size should be | |
* written back to @c textureSize. | |
* @note This can be used in conjunction with computeSourceTextureRect() to select a | |
* subrectangle of the source texture for rendering. | |
* @note The default implementation leaves @c textureSize unmodified. | |
* @param textureSize Actual texture size, also receiving desired texture size | |
*/ | |
virtual void computeDestTextureSize(QSizeF* textureSize) const; | |
/** | |
* @brief Computes the subrectangle within the source Texture to render. | |
* | |
* @c textureSubRect initially contains a unit rectangle from <tt>(0.0f, 0.0f)</tt> to | |
* <tt>(1.0f, 1.0f)</tt>. The desired subrectangle should be written to @c textureSubRect by | |
* this function. | |
* @note This can be used in conjunction with computeDestTextureSize() to select a | |
* subrectangle of the source texture for rendering. | |
* @note The default implementation leaves @c textureSubRect unmodified. | |
* @param textureSubRect | |
*/ | |
virtual void computeSourceTextureRect(QRectF* textureSubRect) const; | |
/** | |
* @brief Marks the texture rectangle as dirty, making ImageRenderer update. | |
*/ | |
void markSourceTextureRectDirty(); | |
private: | |
void reloadTexture(); | |
void updateLocalBoundingBox(); | |
void setCulled(bool value); | |
void updateVisibility(); | |
void checkBody(); | |
bool mVisible = true; | |
bool mWasVisible = true; | |
bool mCacheRenderParams = false; | |
bool mCullingControlsBodyActive = false; | |
QRectF mCustomLocalBox; | |
Actor* mActor = nullptr; | |
Body* mBody = nullptr; | |
float mSizeScale = 1.0f; | |
float mAspectRatio = 1.0f; | |
QRectF mLocalBoundingBox; | |
float mTextureAspectRatio = 1.0f; | |
QString mSource; | |
bool mCulled = false; | |
bool mSourceDirty = false; | |
bool mTransformDirty = true; | |
uint mCurrentTexture = 0; | |
std::vector<TexturePtr> mTextures; | |
bool mHasCachedRenderParams = false; | |
RenderParameters mCachedRenderParams; | |
// Double buffered data for threaded rendering. | |
RenderNode mRenderNode; | |
TexturePtr mRenderTexture; | |
QPointF mRenderPos; | |
QRectF mRenderTextureSubRect; | |
QSizeF mRenderSize; | |
float mRenderRotation = 0.0f; | |
float mRenderOpacity = 1.0f; | |
}; | |
Q_DECLARE_METATYPE(ImageRenderer*) | |
#endif // IMAGERENDERER_H |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment