2019-11-23 20:27:39 +08:00
|
|
|
/****************************************************************************
|
|
|
|
Copyright (c) 2014-2016 Chukong Technologies Inc.
|
|
|
|
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
|
|
|
|
2024-06-10 02:25:43 +08:00
|
|
|
https://axmol.dev/
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
|
|
in the Software without restriction, including without limitation the rights
|
|
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
|
|
all copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
THE SOFTWARE.
|
|
|
|
****************************************************************************/
|
|
|
|
|
2023-06-11 13:08:08 +08:00
|
|
|
#include "3d/BillBoard.h"
|
|
|
|
#include "2d/SpriteFrameCache.h"
|
|
|
|
#include "base/Director.h"
|
|
|
|
#include "2d/Camera.h"
|
|
|
|
#include "renderer/Renderer.h"
|
2019-11-23 20:27:39 +08:00
|
|
|
|
2024-08-26 00:25:33 +08:00
|
|
|
namespace ax
|
|
|
|
{
|
2019-11-23 20:27:39 +08:00
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
BillBoard::BillBoard() : _mode(Mode::VIEW_POINT_ORIENTED), _modeDirty(false)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
_trianglesCommand.setTransparent(true);
|
|
|
|
_trianglesCommand.set3D(true);
|
2021-12-25 10:04:45 +08:00
|
|
|
Node::setAnchorPoint(Vec2(0.5f, 0.5f));
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
BillBoard::~BillBoard() {}
|
2019-11-23 20:27:39 +08:00
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
BillBoard* BillBoard::createWithTexture(Texture2D* texture, Mode mode)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
BillBoard* billboard = new BillBoard();
|
2021-12-08 00:11:53 +08:00
|
|
|
if (billboard->initWithTexture(texture))
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
billboard->_mode = mode;
|
|
|
|
billboard->autorelease();
|
|
|
|
return billboard;
|
|
|
|
}
|
2022-07-16 10:43:05 +08:00
|
|
|
AX_SAFE_DELETE(billboard);
|
2019-11-23 20:27:39 +08:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2021-12-31 12:12:40 +08:00
|
|
|
BillBoard* BillBoard::create(std::string_view filename, Mode mode)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
BillBoard* billboard = new BillBoard();
|
2021-12-08 00:11:53 +08:00
|
|
|
if (billboard->initWithFile(filename))
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
billboard->_mode = mode;
|
|
|
|
billboard->autorelease();
|
|
|
|
return billboard;
|
|
|
|
}
|
2022-07-16 10:43:05 +08:00
|
|
|
AX_SAFE_DELETE(billboard);
|
2019-11-23 20:27:39 +08:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2021-12-31 12:12:40 +08:00
|
|
|
BillBoard* BillBoard::create(std::string_view filename, const Rect& rect, Mode mode)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
BillBoard* billboard = new BillBoard();
|
2021-12-08 00:11:53 +08:00
|
|
|
if (billboard->initWithFile(filename, rect))
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
billboard->_mode = mode;
|
|
|
|
billboard->autorelease();
|
|
|
|
return billboard;
|
|
|
|
}
|
2022-07-16 10:43:05 +08:00
|
|
|
AX_SAFE_DELETE(billboard);
|
2019-11-23 20:27:39 +08:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
BillBoard* BillBoard::create(Mode mode)
|
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
BillBoard* billboard = new BillBoard();
|
2021-12-08 00:11:53 +08:00
|
|
|
if (billboard->init())
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
billboard->_mode = mode;
|
|
|
|
billboard->autorelease();
|
|
|
|
return billboard;
|
|
|
|
}
|
2022-07-16 10:43:05 +08:00
|
|
|
AX_SAFE_DELETE(billboard);
|
2019-11-23 20:27:39 +08:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
void BillBoard::visit(Renderer* renderer, const Mat4& parentTransform, uint32_t parentFlags)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
// quick return if not visible. children won't be drawn.
|
|
|
|
if (!_visible)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
bool visibleByCamera = isVisitableByVisitingCamera();
|
|
|
|
// quick return if not visible by camera and has no children.
|
|
|
|
if (!visibleByCamera && _children.empty())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
uint32_t flags = processParentFlags(parentTransform, parentFlags);
|
2021-12-25 10:04:45 +08:00
|
|
|
|
|
|
|
// Add 3D flag so all the children will be rendered as 3D object
|
2019-11-23 20:27:39 +08:00
|
|
|
flags |= FLAGS_RENDER_AS_3D;
|
2021-12-25 10:04:45 +08:00
|
|
|
|
|
|
|
// Update Billboard transform
|
2019-11-23 20:27:39 +08:00
|
|
|
bool dirty = calculateBillboardTransform();
|
2021-12-25 10:04:45 +08:00
|
|
|
if (dirty)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
flags |= FLAGS_TRANSFORM_DIRTY;
|
|
|
|
}
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2021-04-22 22:01:47 +08:00
|
|
|
_director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
|
|
|
|
_director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform);
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
int i = 0;
|
2021-12-25 10:04:45 +08:00
|
|
|
|
|
|
|
if (!_children.empty())
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
sortAllChildren();
|
|
|
|
// draw children zOrder < 0
|
2021-12-25 10:04:45 +08:00
|
|
|
for (auto size = _children.size(); i < size; ++i)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
auto node = _children.at(i);
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
if (node && node->getLocalZOrder() < 0)
|
|
|
|
node->visit(renderer, _modelViewTransform, flags);
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// self draw
|
|
|
|
if (visibleByCamera)
|
|
|
|
this->draw(renderer, _modelViewTransform, flags);
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
for (auto it = _children.cbegin() + i, itCend = _children.cend(); it != itCend; ++it)
|
2019-11-23 20:27:39 +08:00
|
|
|
(*it)->visit(renderer, _modelViewTransform, flags);
|
|
|
|
}
|
|
|
|
else if (visibleByCamera)
|
|
|
|
{
|
|
|
|
this->draw(renderer, _modelViewTransform, flags);
|
|
|
|
}
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2021-04-22 22:01:47 +08:00
|
|
|
_director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool BillBoard::calculateBillboardTransform()
|
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
// Get camera world position
|
|
|
|
auto camera = Camera::getVisitingCamera();
|
2019-11-23 20:27:39 +08:00
|
|
|
const Mat4& camWorldMat = camera->getNodeToWorldTransform();
|
2021-12-25 10:04:45 +08:00
|
|
|
|
|
|
|
// TODO: use math lib to calculate math lib Make it easier to read and maintain
|
|
|
|
if (memcmp(_camWorldMat.m, camWorldMat.m, sizeof(float) * 16) != 0 ||
|
|
|
|
memcmp(_mvTransform.m, _modelViewTransform.m, sizeof(float) * 16) != 0 || _modeDirty || true)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
// Rotate based on anchor point
|
|
|
|
Vec3 anchorPoint(_anchorPointInPoints.x, _anchorPointInPoints.y, 0.0f);
|
2019-11-23 20:27:39 +08:00
|
|
|
Mat4 localToWorld = _modelViewTransform;
|
|
|
|
localToWorld.translate(anchorPoint);
|
2021-12-25 10:04:45 +08:00
|
|
|
|
|
|
|
// Decide billboard mode
|
2019-11-23 20:27:39 +08:00
|
|
|
Vec3 camDir;
|
|
|
|
switch (_mode)
|
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
case Mode::VIEW_POINT_ORIENTED:
|
|
|
|
camDir.set(localToWorld.m[12] - camWorldMat.m[12], localToWorld.m[13] - camWorldMat.m[13],
|
|
|
|
localToWorld.m[14] - camWorldMat.m[14]);
|
|
|
|
break;
|
|
|
|
case Mode::VIEW_PLANE_ORIENTED:
|
|
|
|
camWorldMat.transformVector(Vec3(0.0f, 0.0f, -1.0f), &camDir);
|
|
|
|
break;
|
|
|
|
default:
|
2022-07-16 10:43:05 +08:00
|
|
|
AXASSERT(false, "invalid billboard mode");
|
2021-12-25 10:04:45 +08:00
|
|
|
break;
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
_modeDirty = false;
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
if (camDir.length() < MATH_TOLERANCE)
|
|
|
|
{
|
|
|
|
camDir.set(camWorldMat.m[8], camWorldMat.m[9], camWorldMat.m[10]);
|
|
|
|
}
|
|
|
|
camDir.normalize();
|
|
|
|
|
|
|
|
Mat4 rotationMatrix;
|
|
|
|
rotationMatrix.setIdentity();
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
Vec3 upAxis(rotationMatrix.m[4], rotationMatrix.m[5], rotationMatrix.m[6]);
|
2019-11-23 20:27:39 +08:00
|
|
|
Vec3 x, y;
|
|
|
|
camWorldMat.transformVector(upAxis, &y);
|
|
|
|
Vec3::cross(camDir, y, &x);
|
|
|
|
x.normalize();
|
|
|
|
Vec3::cross(x, camDir, &y);
|
|
|
|
y.normalize();
|
2021-12-25 10:04:45 +08:00
|
|
|
|
|
|
|
float xlen = sqrtf(localToWorld.m[0] * localToWorld.m[0] + localToWorld.m[1] * localToWorld.m[1] +
|
|
|
|
localToWorld.m[2] * localToWorld.m[2]);
|
|
|
|
float ylen = sqrtf(localToWorld.m[4] * localToWorld.m[4] + localToWorld.m[5] * localToWorld.m[5] +
|
|
|
|
localToWorld.m[6] * localToWorld.m[6]);
|
|
|
|
float zlen = sqrtf(localToWorld.m[8] * localToWorld.m[8] + localToWorld.m[9] * localToWorld.m[9] +
|
|
|
|
localToWorld.m[10] * localToWorld.m[10]);
|
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
Mat4 billboardTransform;
|
2021-12-25 10:04:45 +08:00
|
|
|
|
|
|
|
billboardTransform.m[0] = x.x * xlen;
|
|
|
|
billboardTransform.m[1] = x.y * xlen;
|
|
|
|
billboardTransform.m[2] = x.z * xlen;
|
|
|
|
billboardTransform.m[4] = y.x * ylen;
|
|
|
|
billboardTransform.m[5] = y.y * ylen;
|
|
|
|
billboardTransform.m[6] = y.z * ylen;
|
|
|
|
billboardTransform.m[8] = -camDir.x * zlen;
|
|
|
|
billboardTransform.m[9] = -camDir.y * zlen;
|
|
|
|
billboardTransform.m[10] = -camDir.z * zlen;
|
|
|
|
billboardTransform.m[12] = localToWorld.m[12];
|
|
|
|
billboardTransform.m[13] = localToWorld.m[13];
|
|
|
|
billboardTransform.m[14] = localToWorld.m[14];
|
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
billboardTransform.translate(-anchorPoint);
|
|
|
|
_mvTransform = _modelViewTransform = billboardTransform;
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
_camWorldMat = camWorldMat;
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
return true;
|
|
|
|
}
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
void BillBoard::draw(Renderer* renderer, const Mat4& /*transform*/, uint32_t flags)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
// FIXME: frustum culling here
|
2019-11-23 20:27:39 +08:00
|
|
|
flags |= Node::FLAGS_RENDER_AS_3D;
|
|
|
|
_trianglesCommand.init(0, _texture, _blendFunc, _polyInfo.triangles, _modelViewTransform, flags);
|
2021-12-25 10:04:45 +08:00
|
|
|
setMVPMatrixUniform(); // update uniform
|
2019-11-23 20:27:39 +08:00
|
|
|
renderer->addCommand(&_trianglesCommand);
|
|
|
|
}
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
void BillBoard::setMode(Mode mode)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
_mode = mode;
|
2019-11-23 20:27:39 +08:00
|
|
|
_modeDirty = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
BillBoard::Mode BillBoard::getMode() const
|
|
|
|
{
|
|
|
|
return _mode;
|
|
|
|
}
|
|
|
|
|
2024-08-26 00:25:33 +08:00
|
|
|
}
|