/**************************************************************************** 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 #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 _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 _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[]); - Node::setCameraMask(s_CM[]); 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(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(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(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(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); }