mirror of https://github.com/axmolengine/axmol.git
878 lines
33 KiB
C++
878 lines
33 KiB
C++
/****************************************************************************
|
|
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
|
|
|
https://axmolengine.github.io/
|
|
|
|
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.
|
|
****************************************************************************/
|
|
|
|
#include "Scene3DTest.h"
|
|
|
|
#include "ui/CocosGUI.h"
|
|
#include "renderer/CCRenderState.h"
|
|
#include <spine/spine-cocos2dx.h>
|
|
|
|
#include "../testResource.h"
|
|
#include "../TerrainTest/TerrainTest.h"
|
|
|
|
USING_NS_AX;
|
|
using namespace spine;
|
|
|
|
class SkeletonAnimationCullingFix : public SkeletonAnimation
|
|
{
|
|
public:
|
|
SkeletonAnimationCullingFix() : SkeletonAnimation() {}
|
|
|
|
virtual void draw(axis::Renderer* renderer, const axis::Mat4& transform, uint32_t transformFlags) override
|
|
{
|
|
glDisable(GL_CULL_FACE);
|
|
SkeletonAnimation::draw(renderer, transform, transformFlags);
|
|
RenderState::StateBlock::invalidate(axis::RenderState::StateBlock::RS_ALL_ONES);
|
|
}
|
|
|
|
static SkeletonAnimationCullingFix* createWithFile(std::string_view skeletonDataFile,
|
|
std::string_view atlasFile,
|
|
float scale = 1)
|
|
{
|
|
SkeletonAnimationCullingFix* node = new SkeletonAnimationCullingFix();
|
|
spAtlas* atlas = spAtlas_createFromFile(atlasFile.c_str(), 0);
|
|
node->initWithJsonFile(skeletonDataFile, atlas, scale);
|
|
node->autorelease();
|
|
return node;
|
|
}
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Declare Scene3DTestScene
|
|
|
|
/** Scene3DTestScene designed for test 2D-3D mixed render for common 3D game usage.
|
|
*
|
|
* Scene has three logic sub scenes:
|
|
* - World scene for maintain 3D game world objects, there are two cameras in this
|
|
* scene, one for skybox, another for other 3D models.
|
|
* - UI scene, the root scene, maintain a menu in main UI.
|
|
* - Dialog scene maintain two dialogs, which has 3D models on it and another
|
|
* 2D elements above on 3D models, there are three cameras for them.
|
|
* - OSD scene, maintain the UI element, like the description dialog, above
|
|
* - on other elements.
|
|
*/
|
|
class Scene3DTestScene : public TestCase
|
|
{
|
|
public:
|
|
CREATE_FUNC(Scene3DTestScene);
|
|
|
|
bool onTouchBegan(Touch* touch, Event* event) { return true; }
|
|
void onTouchEnd(Touch*, Event*);
|
|
|
|
private:
|
|
Scene3DTestScene();
|
|
virtual ~Scene3DTestScene();
|
|
bool init() override;
|
|
|
|
void createWorld3D();
|
|
void createUI();
|
|
void createPlayerDlg();
|
|
void createDetailDlg();
|
|
void createDescDlg();
|
|
|
|
// init in init()
|
|
std::vector<Camera*> _gameCameras;
|
|
Node* _worldScene;
|
|
Node* _dlgScene;
|
|
Node* _osdScene;
|
|
|
|
// init in createWorld3D()
|
|
TextureCube* _textureCube;
|
|
Skybox* _skyBox;
|
|
axis::Terrain* _terrain;
|
|
Player* _player;
|
|
Node* _monsters[2];
|
|
|
|
// init in createUI()
|
|
Node* _playerItem;
|
|
Node* _detailItem;
|
|
Node* _descItem;
|
|
Node* _ui;
|
|
|
|
// init in createPlayerDlg()
|
|
Node* _playerDlg;
|
|
// init in createDetailDlg()
|
|
Node* _detailDlg;
|
|
// init in createDescDlg()
|
|
Node* _descDlg;
|
|
enum SkinType
|
|
{
|
|
HAIR = 0,
|
|
GLASSES,
|
|
FACE,
|
|
UPPER_BODY,
|
|
HAND,
|
|
PANTS,
|
|
SHOES,
|
|
MAX_TYPE,
|
|
};
|
|
|
|
std::vector<std::string> _skins[(int)SkinType::MAX_TYPE]; // all skins
|
|
int _curSkin[(int)SkinType::MAX_TYPE]; // current skin index
|
|
axis::MeshRenderer* _reskinGirl;
|
|
|
|
// for capture screen
|
|
static const int SNAPSHOT_TAG = 119;
|
|
std::string _snapshotFile;
|
|
};
|
|
|
|
/** Define the sub scenes in test. */
|
|
enum GAME_SCENE
|
|
{
|
|
SCENE_UI = 0,
|
|
SCENE_WORLD,
|
|
SCENE_DIALOG,
|
|
SCENE_OSD,
|
|
SCENE_COUNT,
|
|
};
|
|
|
|
/** Define the layers in scene, layer separated by camera mask. */
|
|
enum SCENE_LAYER
|
|
{
|
|
LAYER_BACKGROUND = 0,
|
|
LAYER_DEFAULT,
|
|
LAYER_MIDDLE,
|
|
LAYER_TOP,
|
|
LAYER_COUNT,
|
|
};
|
|
|
|
/** Define the all cameras, which in Scene3DTest, render order. */
|
|
enum GAME_CAMERAS_ORDER
|
|
{
|
|
CAMERA_WORLD_3D_SKYBOX = 0,
|
|
CAMERA_WORLD_3D_SCENE,
|
|
CAMERA_UI_2D,
|
|
CAMERA_DIALOG_2D_BASE,
|
|
CAMERA_DIALOG_3D_MODEL,
|
|
CAMERA_DIALOG_2D_ABOVE,
|
|
CAMERA_OSD_2D_BASE,
|
|
CAMERA_OSD_3D_MODEL,
|
|
CAMERA_OSD_2D_ABOVE,
|
|
CAMERA_COUNT,
|
|
};
|
|
|
|
/*
|
|
Defined s_CF and s_CM to avoid force conversion when call Camera::setCameraFlag
|
|
and Node::setCameraMask.
|
|
|
|
Usage:
|
|
- Camera::setCameraFlag(s_CF[<SCENE_LAYER_INDEX>]);
|
|
- Node::setCameraMask(s_CM[<SCENE_LAYER_INDEX>]);
|
|
|
|
Note:
|
|
For LAYER_DEFAULT, we use CameraFlag::DEFAULT, thus we don't need to set
|
|
camera flag/mask for this layer, for other layers we must to set camera
|
|
flag/mask to distinguish between each other.
|
|
*/
|
|
static CameraFlag s_CF[LAYER_COUNT] = {
|
|
CameraFlag::USER1, // LAYER_BACKGROUND
|
|
CameraFlag::DEFAULT, // LAYER_DEFAULT
|
|
CameraFlag::USER3, // LAYER_MIDDLE
|
|
CameraFlag::USER4, // LAYER_TOP
|
|
};
|
|
|
|
static unsigned short s_CM[LAYER_COUNT] = {
|
|
(unsigned short)s_CF[0],
|
|
(unsigned short)s_CF[1],
|
|
(unsigned short)s_CF[2],
|
|
(unsigned short)s_CF[3],
|
|
};
|
|
|
|
static const char* s_CameraNames[CAMERA_COUNT] = {"World 3D Skybox", "World 3D Scene", "UI 2D",
|
|
"Dialog 2D Base", "Dialog 3D Model", "Dialog 2D Above",
|
|
"OSD 2D Base", "OSD 3D Model", "OSD 2D Above"};
|
|
|
|
/** The scenes, located in different position, won't see each other. */
|
|
static Vec3 s_scenePositons[SCENE_COUNT] = {
|
|
Vec3(0, 0, 0), // center : UI scene
|
|
Vec3(0, 10000, 0), // top : World sub scene
|
|
Vec3(10000, 0, 0), // right : Dialog sub scene
|
|
Vec3(0, -10000, 0), // bottom : OSD sub scene
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Implements Scene3DTestScene
|
|
|
|
Scene3DTestScene::Scene3DTestScene()
|
|
: _worldScene(nullptr)
|
|
, _dlgScene(nullptr)
|
|
, _osdScene(nullptr)
|
|
, _textureCube(nullptr)
|
|
, _skyBox(nullptr)
|
|
, _terrain(nullptr)
|
|
, _player(nullptr)
|
|
, _playerItem(nullptr)
|
|
, _detailItem(nullptr)
|
|
, _descItem(nullptr)
|
|
, _ui(nullptr)
|
|
, _playerDlg(nullptr)
|
|
, _detailDlg(nullptr)
|
|
, _descDlg(nullptr)
|
|
{
|
|
_monsters[0] = _monsters[1] = nullptr;
|
|
}
|
|
|
|
Scene3DTestScene::~Scene3DTestScene() {}
|
|
|
|
bool Scene3DTestScene::init()
|
|
{
|
|
bool ret = false;
|
|
do
|
|
{
|
|
AX_BREAK_IF(false == TestCase::init());
|
|
|
|
// prepare for camera creation, we need several custom cameras
|
|
_gameCameras.resize(CAMERA_COUNT);
|
|
auto visibleSize = Director::getInstance()->getVisibleSize();
|
|
Camera* ca = nullptr; // temp variable
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// create world 3D scene, this scene has two camera
|
|
_worldScene = Node::create();
|
|
// create a camera to look the skybox
|
|
ca = _gameCameras[CAMERA_WORLD_3D_SKYBOX] =
|
|
Camera::createPerspective(60, visibleSize.width / visibleSize.height, 10, 1000);
|
|
ca->setDepth(CAMERA_WORLD_3D_SKYBOX);
|
|
ca->setName(s_CameraNames[CAMERA_WORLD_3D_SKYBOX]);
|
|
ca->setCameraFlag(s_CF[LAYER_BACKGROUND]);
|
|
ca->setPosition3D(Vec3(0.f, 0.f, 50.f));
|
|
_worldScene->addChild(ca);
|
|
// create a camera to look the 3D models in world 3D scene
|
|
ca = _gameCameras[CAMERA_WORLD_3D_SCENE] =
|
|
Camera::createPerspective(60, visibleSize.width / visibleSize.height, 0.1f, 200);
|
|
ca->setDepth(CAMERA_WORLD_3D_SCENE);
|
|
ca->setName(s_CameraNames[CAMERA_WORLD_3D_SCENE]);
|
|
_worldScene->addChild(ca);
|
|
// create 3D objects and add to world scene
|
|
createWorld3D();
|
|
_worldScene->addChild(_skyBox);
|
|
_worldScene->addChild(_terrain);
|
|
_worldScene->addChild(_player);
|
|
_worldScene->addChild(_monsters[0]);
|
|
_worldScene->addChild(_monsters[1]);
|
|
// move camera above player
|
|
ca->setPosition3D(_player->getPosition3D() + Vec3(0, 45, 60));
|
|
ca->setRotation3D(Vec3(-45, 0, 0));
|
|
_worldScene->setPosition3D(s_scenePositons[SCENE_WORLD]);
|
|
this->addChild(_worldScene);
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// test scene is UI scene, use default camera
|
|
// use the default camera to look 2D base UI layer
|
|
ca = _gameCameras[CAMERA_UI_2D] = this->getDefaultCamera();
|
|
ca->setDepth(CAMERA_UI_2D);
|
|
ca->setName(s_CameraNames[CAMERA_UI_2D]);
|
|
// create UI element and add to ui scene
|
|
createUI();
|
|
this->addChild(_ui);
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// create dialog scene, this scene has two dialog and three cameras
|
|
_dlgScene = Node::create();
|
|
// use default camera to render the base 2D elements
|
|
ca = _gameCameras[CAMERA_DIALOG_2D_BASE] = Camera::create();
|
|
ca->setDepth(CAMERA_DIALOG_2D_BASE);
|
|
ca->setName(s_CameraNames[CAMERA_DIALOG_2D_BASE]);
|
|
_dlgScene->addChild(ca);
|
|
// create a camera to look the 3D model in dialog scene
|
|
ca = _gameCameras[CAMERA_DIALOG_3D_MODEL] = Camera::create();
|
|
ca->setDepth(CAMERA_DIALOG_3D_MODEL);
|
|
ca->setName(s_CameraNames[CAMERA_DIALOG_3D_MODEL]);
|
|
ca->setCameraFlag(s_CF[LAYER_MIDDLE]);
|
|
_dlgScene->addChild(ca);
|
|
// create a camera to look the UI element over on the 3D models
|
|
ca = _gameCameras[CAMERA_DIALOG_2D_ABOVE] = Camera::create();
|
|
ca->setDepth(CAMERA_DIALOG_2D_ABOVE);
|
|
ca->setName(s_CameraNames[CAMERA_DIALOG_2D_ABOVE]);
|
|
ca->setCameraFlag(s_CF[LAYER_TOP]);
|
|
_dlgScene->addChild(ca);
|
|
// create dialogs and add to dialog scene
|
|
createPlayerDlg();
|
|
_dlgScene->addChild(_playerDlg);
|
|
createDetailDlg();
|
|
_dlgScene->addChild(_detailDlg);
|
|
// add dialog scene to test scene, which can't see the other element
|
|
_dlgScene->setPosition3D(s_scenePositons[SCENE_DIALOG]);
|
|
this->addChild(_dlgScene);
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// create description scene, this scene has a dialog and three cameras
|
|
_osdScene = Node::create();
|
|
// use default camera for render 2D element
|
|
ca = _gameCameras[CAMERA_OSD_2D_BASE] = Camera::create();
|
|
ca->setDepth(CAMERA_OSD_2D_BASE);
|
|
ca->setName(s_CameraNames[CAMERA_OSD_2D_BASE]);
|
|
_osdScene->addChild(ca);
|
|
// create a camera to look the 3D model in dialog scene
|
|
ca = _gameCameras[CAMERA_OSD_3D_MODEL] = Camera::create();
|
|
ca->setDepth(CAMERA_OSD_3D_MODEL);
|
|
ca->setName(s_CameraNames[CAMERA_OSD_3D_MODEL]);
|
|
ca->setCameraFlag(s_CF[LAYER_MIDDLE]);
|
|
_osdScene->addChild(ca);
|
|
// create a camera to look the UI element over on the 3D models
|
|
ca = _gameCameras[CAMERA_OSD_2D_ABOVE] = Camera::create();
|
|
ca->setDepth(CAMERA_OSD_2D_ABOVE);
|
|
ca->setName(s_CameraNames[CAMERA_OSD_2D_ABOVE]);
|
|
ca->setCameraFlag(s_CF[LAYER_TOP]);
|
|
_osdScene->addChild(ca);
|
|
// create desc dialog and add to osd scene
|
|
createDescDlg();
|
|
_osdScene->addChild(_descDlg);
|
|
// add osd scene to test scene, which can't see the other elements
|
|
_osdScene->setPosition3D(s_scenePositons[SCENE_OSD]);
|
|
this->addChild(_osdScene);
|
|
|
|
_playerDlg->setVisible(false);
|
|
_detailDlg->setVisible(false);
|
|
_descDlg->setVisible(false);
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// add touch event callback
|
|
auto listener = EventListenerTouchOneByOne::create();
|
|
listener->onTouchBegan = AX_CALLBACK_2(Scene3DTestScene::onTouchBegan, this);
|
|
listener->onTouchEnded = AX_CALLBACK_2(Scene3DTestScene::onTouchEnd, this);
|
|
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
|
|
|
|
ret = true;
|
|
} while (0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void Scene3DTestScene::createWorld3D()
|
|
{
|
|
// create skybox
|
|
// create and set our custom shader
|
|
auto shader = GLProgram::createWithFilenames("MeshRendererTest/cube_map.vert", "MeshRendererTest/cube_map.frag");
|
|
auto state = GLProgramState::create(shader);
|
|
|
|
// create the second texture for cylinder
|
|
_textureCube = TextureCube::create("MeshRendererTest/skybox/left.jpg", "MeshRendererTest/skybox/right.jpg",
|
|
"MeshRendererTest/skybox/top.jpg", "MeshRendererTest/skybox/bottom.jpg",
|
|
"MeshRendererTest/skybox/front.jpg", "MeshRendererTest/skybox/back.jpg");
|
|
// set texture parameters
|
|
Texture2D::TexParams tRepeatParams;
|
|
tRepeatParams.magFilter = GL_LINEAR;
|
|
tRepeatParams.minFilter = GL_LINEAR;
|
|
tRepeatParams.sAddressMode = GL_MIRRORED_REPEAT;
|
|
tRepeatParams.tAddressMode = GL_MIRRORED_REPEAT;
|
|
_textureCube->setTexParameters(tRepeatParams);
|
|
|
|
// pass the texture sampler to our custom shader
|
|
state->setUniformTexture("u_cubeTex", _textureCube);
|
|
|
|
// add skybox
|
|
_skyBox = Skybox::create();
|
|
_skyBox->setCameraMask(s_CM[LAYER_BACKGROUND]);
|
|
_skyBox->setTexture(_textureCube);
|
|
|
|
// create terrain
|
|
Terrain::DetailMap r("TerrainTest/dirt.jpg");
|
|
Terrain::DetailMap g("TerrainTest/Grass2.jpg", 10);
|
|
Terrain::DetailMap b("TerrainTest/road.jpg");
|
|
Terrain::DetailMap a("TerrainTest/GreenSkin.jpg", 20);
|
|
Terrain::TerrainData data("TerrainTest/heightmap16.jpg", "TerrainTest/alphamap.png", r, g, b, a, Size(32, 32),
|
|
40.0f, 2);
|
|
_terrain = Terrain::create(data, Terrain::CrackFixedType::SKIRT);
|
|
_terrain->setMaxDetailMapAmount(4);
|
|
_terrain->setDrawWire(false);
|
|
|
|
_terrain->setSkirtHeightRatio(3);
|
|
_terrain->setLODDistance(64, 128, 192);
|
|
|
|
// create player
|
|
_player = Player::create("MeshRendererTest/girl.c3b", _gameCameras[CAMERA_WORLD_3D_SCENE], _terrain);
|
|
_player->setScale(0.08f);
|
|
_player->setPositionY(_terrain->getHeight(_player->getPositionX(), _player->getPositionZ()));
|
|
|
|
auto animation = Animation3D::create("MeshRendererTest/girl.c3b", "Take 001");
|
|
if (animation)
|
|
{
|
|
auto animate = Animate3D::create(animation);
|
|
_player->runAction(RepeatForever::create(animate));
|
|
}
|
|
// add a particle 3d above player
|
|
auto rootps =
|
|
PUParticleSystem3D::create("Particle3D/scripts/blackHole.pu", "Particle3D/materials/pu_mediapack_01.material");
|
|
rootps->setScale(2);
|
|
rootps->setPosition3D(Vec3(0, 150, 0));
|
|
auto moveby = MoveBy::create(2.0f, Vec2(50.0f, 0.0f));
|
|
auto moveby1 = MoveBy::create(2.0f, Vec2(-50.0f, 0.0f));
|
|
rootps->runAction(RepeatForever::create(Sequence::create(moveby, moveby1, nullptr)));
|
|
rootps->startParticleSystem();
|
|
|
|
_player->addChild(rootps, 0);
|
|
|
|
// add BillBoard for test blend
|
|
auto billboard = BillBoard::create("Images/btn-play-normal.png");
|
|
billboard->setPosition3D(Vec3(0, 180, 0));
|
|
_player->addChild(billboard);
|
|
|
|
// create two MeshRenderer monster, one is transparent
|
|
auto monster = MeshRenderer::create("MeshRendererTest/orc.c3b");
|
|
monster->setRotation3D(Vec3(0, 180, 0));
|
|
monster->setPosition3D(_player->getPosition3D() + Vec3(50, -10, 0));
|
|
monster->setOpacity(128);
|
|
_monsters[0] = monster;
|
|
monster = MeshRenderer::create("MeshRendererTest/orc.c3b");
|
|
monster->setRotation3D(Vec3(0, 180, 0));
|
|
monster->setPosition3D(_player->getPosition3D() + Vec3(-50, -5, 0));
|
|
_monsters[1] = monster;
|
|
}
|
|
|
|
void Scene3DTestScene::createUI()
|
|
{
|
|
_ui = Layer::create();
|
|
|
|
// first, add menu to ui
|
|
// create player button
|
|
auto showPlayerDlgItem = MenuItemImage::create("Images/Pea.png", "Images/Pea.png", [this](Ref* sender) {
|
|
this->_playerDlg->setVisible(!this->_playerDlg->isVisible());
|
|
});
|
|
showPlayerDlgItem->setName("showPlayerDlgItem");
|
|
showPlayerDlgItem->setPosition(VisibleRect::left().x + 30, VisibleRect::top().y - 30);
|
|
|
|
// create description button
|
|
TTFConfig ttfConfig("fonts/arial.ttf", 20);
|
|
auto descItem = MenuItemLabel::create(Label::createWithTTF(ttfConfig, "Description"), [this](Ref* sender) {
|
|
if (this->_descDlg->isVisible())
|
|
{
|
|
// hide descDlg
|
|
_descDlg->setVisible(false);
|
|
}
|
|
else
|
|
{
|
|
// animate show descDlg
|
|
_descDlg->setVisible(true);
|
|
_descDlg->setScale(0);
|
|
_descDlg->runAction(ScaleTo::create(2, 1.0));
|
|
}
|
|
});
|
|
descItem->setName("descItem");
|
|
descItem->setPosition(Vec2(VisibleRect::right().x - 50, VisibleRect::top().y - 25));
|
|
|
|
auto menu = Menu::create(showPlayerDlgItem, descItem, nullptr);
|
|
menu->setPosition(Vec2::ZERO);
|
|
_ui->addChild(menu);
|
|
|
|
// second, add cameras control button to ui
|
|
auto createCameraButton = [this](int tag, const char* text) -> Node* {
|
|
auto cb = ui::CheckBox::create("cocosui/check_box_normal.png", "cocosui/check_box_normal_press.png",
|
|
"cocosui/check_box_active.png", "cocosui/check_box_normal_disable.png",
|
|
"cocosui/check_box_active_disable.png");
|
|
cb->setTag(tag);
|
|
cb->setSelected(true);
|
|
if (text)
|
|
cb->setName(text);
|
|
cb->setAnchorPoint(Vec2(0, 0.5));
|
|
cb->setScale(0.8f);
|
|
cb->addClickEventListener([this](Ref* sender) {
|
|
auto index = static_cast<Node*>(sender)->getTag();
|
|
auto camera = this->_gameCameras[index];
|
|
camera->setVisible(!camera->isVisible());
|
|
});
|
|
if (text)
|
|
{
|
|
auto label = ui::Text::create();
|
|
label->setString(text);
|
|
label->setAnchorPoint(Vec2(0, 0));
|
|
label->setPositionX(cb->getContentSize().width);
|
|
cb->addChild(label);
|
|
}
|
|
return cb;
|
|
};
|
|
|
|
Vec2 pos = VisibleRect::leftBottom();
|
|
pos.y += 30;
|
|
float stepY = 25;
|
|
for (int i = CAMERA_COUNT - 1; i >= 0; --i)
|
|
{
|
|
// if hide CAMERA_UI_2D, the menu won't show again
|
|
// so skip it
|
|
if (CAMERA_UI_2D == i)
|
|
{
|
|
continue;
|
|
}
|
|
auto cameraBtn = createCameraButton(i, s_CameraNames[i]);
|
|
cameraBtn->setPosition(pos);
|
|
_ui->addChild(cameraBtn);
|
|
pos.y += stepY;
|
|
}
|
|
auto labelCameras = ui::Text::create();
|
|
labelCameras->setString("Cameras");
|
|
labelCameras->setAnchorPoint(Vec2(0, 0));
|
|
labelCameras->setPosition(pos);
|
|
_ui->addChild(labelCameras);
|
|
}
|
|
|
|
void Scene3DTestScene::createPlayerDlg()
|
|
{
|
|
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(s_s9s_ui_plist);
|
|
|
|
Size dlgSize(190, 240);
|
|
Vec2 pos = VisibleRect::center();
|
|
float margin = 10;
|
|
|
|
// first, create dialog ui part, include background, title and buttons
|
|
_playerDlg = ui::Scale9Sprite::createWithSpriteFrameName("button_actived.png");
|
|
_playerDlg->setContentSize(dlgSize);
|
|
_playerDlg->setAnchorPoint(Vec2(1, 0.5));
|
|
pos.y -= margin;
|
|
pos.x -= margin;
|
|
_playerDlg->setPosition(pos);
|
|
|
|
// title
|
|
auto title = Label::createWithTTF("Player Dialog", "fonts/arial.ttf", 16);
|
|
title->setPosition(dlgSize.width / 2, dlgSize.height - margin * 2);
|
|
_playerDlg->addChild(title);
|
|
|
|
// player background
|
|
Size bgSize(110, 180);
|
|
Vec2 bgPos(margin, dlgSize.height / 2 - margin);
|
|
auto playerBg = ui::Scale9Sprite::createWithSpriteFrameName("item_bg.png");
|
|
playerBg->setContentSize(bgSize);
|
|
playerBg->setAnchorPoint(Vec2(0, 0.5));
|
|
playerBg->setPosition(bgPos);
|
|
_playerDlg->addChild(playerBg);
|
|
|
|
// item background and item
|
|
Size itemSize(48, 48);
|
|
Vec2 itemAnchor(0, 1);
|
|
Vec2 itemPos(bgPos.x + bgSize.width + margin, bgPos.y + bgSize.height / 2);
|
|
auto itemBg = ui::Scale9Sprite::createWithSpriteFrameName("item_bg.png");
|
|
itemBg->setContentSize(itemSize);
|
|
itemBg->setAnchorPoint(itemAnchor);
|
|
itemBg->setPosition(itemPos);
|
|
_playerDlg->addChild(itemBg);
|
|
|
|
auto item = ui::Button::create("crystal.png", "", "", ui::Widget::TextureResType::PLIST);
|
|
item->setTitleText("Crystal");
|
|
item->setScale(1.5);
|
|
item->setAnchorPoint(itemAnchor);
|
|
item->setPosition(itemPos);
|
|
item->addClickEventListener([this](Ref* sender) { this->_detailDlg->setVisible(!this->_detailDlg->isVisible()); });
|
|
_playerDlg->addChild(item);
|
|
|
|
// second, add 3d actor, which on dialog layer
|
|
std::string filename = "MeshRendererTest/girl.c3b";
|
|
auto girl = MeshRenderer::create(filename);
|
|
girl->setScale(0.5);
|
|
girl->setPosition(bgSize.width / 2, margin * 2);
|
|
girl->setCameraMask(s_CM[LAYER_MIDDLE]);
|
|
playerBg->addChild(girl);
|
|
|
|
// third, add zoom in/out button, which is 2d ui element and over 3d actor
|
|
ui::Button* zoomIn = ui::Button::create("cocosui/animationbuttonnormal.png", "cocosui/animationbuttonpressed.png");
|
|
zoomIn->setScale(0.5);
|
|
zoomIn->setAnchorPoint(Vec2(1, 1));
|
|
zoomIn->setPosition(Vec2(bgSize.width / 2 - margin / 2, bgSize.height - margin));
|
|
zoomIn->addClickEventListener([girl](Ref* sender) { girl->setScale(girl->getScale() * 2); });
|
|
zoomIn->setTitleText("Zoom In");
|
|
zoomIn->setName("Zoom In");
|
|
zoomIn->setCameraMask(s_CM[LAYER_TOP]);
|
|
playerBg->addChild(zoomIn);
|
|
|
|
ui::Button* zoomOut = ui::Button::create("cocosui/animationbuttonnormal.png", "cocosui/animationbuttonpressed.png");
|
|
zoomOut->setScale(0.5);
|
|
zoomOut->setAnchorPoint(Vec2(0, 1));
|
|
zoomOut->setPosition(Vec2(bgSize.width / 2 + margin / 2, bgSize.height - margin));
|
|
zoomOut->addClickEventListener([girl](Ref* sender) { girl->setScale(girl->getScale() / 2); });
|
|
zoomOut->setTitleText("Zoom Out");
|
|
zoomOut->setName("Zoom Out");
|
|
zoomOut->setCameraMask(s_CM[LAYER_TOP]);
|
|
playerBg->addChild(zoomOut);
|
|
|
|
// forth, add slider bar
|
|
ui::Slider* slider = ui::Slider::create("cocosui/slidbar.png", "cocosui/sliderballnormal.png");
|
|
slider->setScale9Enabled(true);
|
|
slider->setPosition(Vec2(bgSize.width / 2, margin));
|
|
slider->setContentSize(Size(bgSize.width - margin, slider->getContentSize().height));
|
|
slider->addEventListener([girl, slider](Ref* sender, ui::Slider::EventType type) {
|
|
girl->setRotation3D(Vec3(0, 360 * slider->getPercent() / 100, 0));
|
|
});
|
|
slider->setName("Slider");
|
|
slider->setCameraMask(s_CM[LAYER_TOP]);
|
|
playerBg->addChild(slider);
|
|
}
|
|
|
|
void Scene3DTestScene::createDetailDlg()
|
|
{
|
|
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(s_s9s_ui_plist);
|
|
|
|
Size dlgSize(190, 240);
|
|
Vec2 pos = VisibleRect::center();
|
|
float margin = 10;
|
|
|
|
// create dialog
|
|
// use Scale9Sprite as background, it won't swallow touch event
|
|
_detailDlg = ui::Scale9Sprite::createWithSpriteFrameName("button_actived.png");
|
|
_detailDlg->setContentSize(dlgSize);
|
|
_detailDlg->setAnchorPoint(Vec2(0, 0.5));
|
|
_detailDlg->setOpacity(224);
|
|
pos.y -= margin;
|
|
pos.x += margin;
|
|
_detailDlg->setPosition(pos);
|
|
|
|
// title
|
|
auto title = Label::createWithTTF("Detail Dialog", "fonts/arial.ttf", 16);
|
|
title->setPosition(dlgSize.width / 2, dlgSize.height - margin * 2);
|
|
_detailDlg->addChild(title);
|
|
|
|
// add capture screen buttons
|
|
ui::Button* capture = ui::Button::create("cocosui/animationbuttonnormal.png", "cocosui/animationbuttonpressed.png");
|
|
capture->setScale(0.5);
|
|
capture->setAnchorPoint(Vec2(0.5, 0));
|
|
capture->setPosition(Vec2(dlgSize.width / 3, margin));
|
|
capture->addClickEventListener([this](Ref* sender) {
|
|
Director::getInstance()->getTextureCache()->removeTextureForKey(_snapshotFile);
|
|
_osdScene->removeChildByTag(SNAPSHOT_TAG);
|
|
_snapshotFile = "CaptureScreenTest.png";
|
|
utils::captureScreen(
|
|
[this](bool succeed, std::string_view outputFile) {
|
|
if (!succeed)
|
|
{
|
|
log("Capture screen failed.");
|
|
return;
|
|
}
|
|
auto sp = Sprite::create(outputFile);
|
|
_osdScene->addChild(sp, 0, SNAPSHOT_TAG);
|
|
Size s = Director::getInstance()->getWinSize();
|
|
sp->setPosition(s.width / 2, s.height / 2);
|
|
sp->setScale(0.25);
|
|
_snapshotFile = outputFile;
|
|
},
|
|
_snapshotFile);
|
|
});
|
|
capture->setTitleText("Take Snapshot");
|
|
capture->setName("Take Snapshot");
|
|
_detailDlg->addChild(capture);
|
|
|
|
ui::Button* remove = ui::Button::create("cocosui/animationbuttonnormal.png", "cocosui/animationbuttonpressed.png");
|
|
remove->setScale(0.5);
|
|
remove->setAnchorPoint(Vec2(0.5, 0));
|
|
remove->setPosition(Vec2(dlgSize.width * 2 / 3, margin));
|
|
remove->addClickEventListener([this](Ref* sender) { _osdScene->removeChildByTag(SNAPSHOT_TAG); });
|
|
remove->setTitleText("Del Snapshot");
|
|
remove->setName("Del Snapshot");
|
|
_detailDlg->addChild(remove);
|
|
|
|
// add a spine ffd animation on it
|
|
auto skeletonNode =
|
|
SkeletonAnimationCullingFix::createWithFile("spine/goblins-pro.json", "spine/goblins.atlas", 1.5f);
|
|
skeletonNode->setAnimation(0, "walk", true);
|
|
skeletonNode->setSkin("goblin");
|
|
|
|
skeletonNode->setScale(0.25);
|
|
Size windowSize = Director::getInstance()->getWinSize();
|
|
skeletonNode->setPosition(Vec2(dlgSize.width / 2, remove->getContentSize().height / 2 + 2 * margin));
|
|
_detailDlg->addChild(skeletonNode);
|
|
}
|
|
|
|
void Scene3DTestScene::createDescDlg()
|
|
{
|
|
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(s_s9s_ui_plist);
|
|
|
|
Size dlgSize(440, 240);
|
|
Vec2 pos = VisibleRect::center();
|
|
float margin = 10;
|
|
|
|
// first, create dialog, add title and description text on it
|
|
// use Layout, which setTouchEnabled(true), as background, it will swallow touch event
|
|
auto desdDlg = ui::Layout::create();
|
|
desdDlg->setBackGroundImageScale9Enabled(true);
|
|
desdDlg->setBackGroundImage("button_actived.png", ui::Widget::TextureResType::PLIST);
|
|
desdDlg->setContentSize(dlgSize);
|
|
desdDlg->setAnchorPoint(Vec2(0.5f, 0.5f));
|
|
desdDlg->setOpacity(224);
|
|
desdDlg->setPosition(pos);
|
|
desdDlg->setTouchEnabled(true);
|
|
_descDlg = desdDlg;
|
|
|
|
// title
|
|
auto title = Label::createWithTTF("Description Dialog", "fonts/arial.ttf", 16);
|
|
title->setPosition(dlgSize.width / 2, dlgSize.height - margin * 2);
|
|
_descDlg->addChild(title);
|
|
|
|
// add a label to retain description text
|
|
Size textSize(400, 220);
|
|
Vec2 textPos(margin, dlgSize.height - (20 + margin));
|
|
std::string desc = std::string(
|
|
" Scene 3D test for 2D and 3D mix rendering.\n"
|
|
"- Game world composite with terrain, skybox and 3D objects.\n"
|
|
"- UI composite with 2D nodes.\n"
|
|
"- Click the icon at the topleft conner, will show a player dialog which "
|
|
"there is a 3D sprite on it.\n"
|
|
"- There are two button to zoom the player model, which should keep above "
|
|
"on 3D model.\n"
|
|
" - This description dialog should above all other elements.\n"
|
|
"\n"
|
|
" Game scene composite with root scene and three sub scene. These scene "
|
|
" located at different location, they can't see each other.\n"
|
|
"- Root scene contains ui layer\n"
|
|
"- World scene contains skybox and 3d scene.\n"
|
|
"- Dialog scene contains actor dialog and detail dialog.\n"
|
|
"- OSD scene contains description dialog.\n"
|
|
"\n"
|
|
"Click \"Description\" button to hide this dialog.\n");
|
|
auto text = Label::createWithSystemFont(desc, "", 9, textSize);
|
|
text->setAnchorPoint(Vec2(0, 1));
|
|
text->setPosition(textPos);
|
|
_descDlg->addChild(text);
|
|
|
|
// second, add a 3D model
|
|
std::string fileName = "MeshRendererTest/ReskinGirl.c3b";
|
|
Vec2 girlPos(textPos.x + textSize.width - 40, margin);
|
|
_reskinGirl = MeshRenderer::create(fileName);
|
|
_reskinGirl->setCameraMask(s_CM[LAYER_MIDDLE]);
|
|
_reskinGirl->setScale(2.5);
|
|
_reskinGirl->setPosition(girlPos);
|
|
_descDlg->addChild(_reskinGirl);
|
|
auto animation = Animation3D::create(fileName);
|
|
if (animation)
|
|
{
|
|
auto animate = Animate3D::create(animation);
|
|
|
|
_reskinGirl->runAction(RepeatForever::create(animate));
|
|
}
|
|
|
|
auto& body = _skins[(int)SkinType::UPPER_BODY];
|
|
body.emplace_back("Girl_UpperBody01");
|
|
body.emplace_back("Girl_UpperBody02");
|
|
|
|
auto& pants = _skins[(int)SkinType::PANTS];
|
|
pants.emplace_back("Girl_LowerBody01");
|
|
pants.emplace_back("Girl_LowerBody02");
|
|
|
|
auto& shoes = _skins[(int)SkinType::SHOES];
|
|
shoes.emplace_back("Girl_Shoes01");
|
|
shoes.emplace_back("Girl_Shoes02");
|
|
|
|
auto& hair = _skins[(int)SkinType::HAIR];
|
|
hair.emplace_back("Girl_Hair01");
|
|
hair.emplace_back("Girl_Hair02");
|
|
|
|
auto& face = _skins[(int)SkinType::FACE];
|
|
face.emplace_back("Girl_Face01");
|
|
face.emplace_back("Girl_Face02");
|
|
|
|
auto& hand = _skins[(int)SkinType::HAND];
|
|
hand.emplace_back("Girl_Hand01");
|
|
hand.emplace_back("Girl_Hand02");
|
|
|
|
auto& glasses = _skins[(int)SkinType::GLASSES];
|
|
glasses.emplace_back("");
|
|
glasses.emplace_back("Girl_Glasses01");
|
|
|
|
memset(_curSkin, 0, (int)SkinType::MAX_TYPE * sizeof(int));
|
|
|
|
auto applyCurSkin = [this]() {
|
|
for (ssize_t i = 0; i < this->_reskinGirl->getMeshCount(); i++)
|
|
{
|
|
auto mesh = this->_reskinGirl->getMeshByIndex(static_cast<int>(i));
|
|
bool isVisible = false;
|
|
for (int j = 0; j < (int)SkinType::MAX_TYPE; j++)
|
|
{
|
|
if (mesh->getName() == _skins[j].at(_curSkin[j]))
|
|
{
|
|
isVisible = true;
|
|
break;
|
|
}
|
|
}
|
|
this->_reskinGirl->getMeshByIndex(static_cast<int>(i))->setVisible(isVisible);
|
|
}
|
|
};
|
|
applyCurSkin();
|
|
|
|
// third, add reskin buttons above 3D model
|
|
static const std::string btnTexts[SkinType::MAX_TYPE] = {
|
|
"Hair", "Glasses", "Face", "Coat", "Hand", "Pants", "Shoes",
|
|
};
|
|
Vec2 btnPos(dlgSize.width - margin, margin);
|
|
for (int i = SkinType::MAX_TYPE - 1; i >= 0; --i)
|
|
{
|
|
auto btn = ui::Button::create("cocosui/animationbuttonnormal.png", "cocosui/animationbuttonpressed.png");
|
|
btn->setScale(0.5);
|
|
btn->setTag(i);
|
|
btn->setAnchorPoint(Vec2(1, 0));
|
|
btn->setPosition(btnPos);
|
|
btnPos.y += 20;
|
|
btn->addClickEventListener([this, applyCurSkin](Ref* sender) {
|
|
auto index = static_cast<Node*>(sender)->getTag();
|
|
if (index < SkinType::MAX_TYPE)
|
|
{
|
|
_curSkin[index] = (_curSkin[index] + 1) % _skins[index].size();
|
|
applyCurSkin();
|
|
}
|
|
});
|
|
btn->setTitleText(btnTexts[i]);
|
|
btn->setCameraMask(s_CM[LAYER_TOP]);
|
|
_descDlg->addChild(btn);
|
|
}
|
|
}
|
|
|
|
void Scene3DTestScene::onTouchEnd(Touch* touch, Event* event)
|
|
{
|
|
auto location = touch->getLocation();
|
|
auto camera = _gameCameras[CAMERA_WORLD_3D_SCENE];
|
|
if (camera != Camera::getVisitingCamera())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (_player)
|
|
{
|
|
Vec3 nearP(location.x, location.y, 0.0f), farP(location.x, location.y, 1.0f);
|
|
// convert screen touch location to the world location on near and far plane
|
|
auto size = Director::getInstance()->getWinSize();
|
|
camera->unprojectGL(size, &nearP, &nearP);
|
|
camera->unprojectGL(size, &farP, &farP);
|
|
Vec3 dir = farP - nearP;
|
|
dir.normalize();
|
|
Vec3 collisionPoint;
|
|
bool isInTerrain = _terrain->getIntersectionPoint(Ray(nearP, dir), collisionPoint);
|
|
if (!isInTerrain)
|
|
{
|
|
_player->idle();
|
|
}
|
|
else
|
|
{
|
|
dir = collisionPoint - _player->getPosition3D();
|
|
dir.y = 0;
|
|
dir.normalize();
|
|
_player->_headingAngle = -1 * acos(dir.dot(Vec3(0, 0, -1)));
|
|
dir.cross(dir, Vec3(0, 0, -1), &_player->_headingAxis);
|
|
_player->_targetPos = collisionPoint;
|
|
_player->forward();
|
|
}
|
|
}
|
|
event->stopPropagation();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Implements Scene3DTests
|
|
Scene3DTests::Scene3DTests()
|
|
{
|
|
ADD_TEST_CASE(Scene3DTestScene);
|
|
}
|