|
|
@@ -2,6 +2,13 @@
|
|
|
* Copyright (C) 2022 The V-Gears Team
|
|
|
*
|
|
|
* This file is part of V-Gears
|
|
|
+ static bool IsJunonTexture(uint16 texture_id){
|
|
|
+ const size_t texture_count = sizeof(textures) / sizeof(textures[0]);
|
|
|
+ if (texture_id >= texture_count) return false;
|
|
|
+ const std::string texture_name = textures[texture_id].name;
|
|
|
+ return texture_name.compare(0, 3, "jun") == 0
|
|
|
+ || texture_name == "jutmpl01";
|
|
|
+ }
|
|
|
*
|
|
|
* V-Gears is free software: you can redistribute it and/or modify it under
|
|
|
* terms of the GNU General Public License as published by the Free Software
|
|
|
@@ -15,21 +22,33 @@
|
|
|
|
|
|
#include "WorldmapModule.h"
|
|
|
|
|
|
+#include <algorithm>
|
|
|
+#include <map>
|
|
|
+#include <memory>
|
|
|
+#include <limits>
|
|
|
+#include <set>
|
|
|
+#include <string>
|
|
|
+#include <vector>
|
|
|
+#include <OgreEntity.h>
|
|
|
#include <OgreSceneManager.h>
|
|
|
#include <OgreManualObject.h>
|
|
|
#include <OgreRoot.h>
|
|
|
+#include <OgreException.h>
|
|
|
#include <OgreHardwarePixelBuffer.h>
|
|
|
-#include <OgreEntity.h>
|
|
|
#include <OgreLogManager.h>
|
|
|
#include <OgreTextureManager.h>
|
|
|
#include <OgreMaterialManager.h>
|
|
|
-#include <OgreTechnique.h>
|
|
|
+#include <OgreResourceGroupManager.h>
|
|
|
+#include "data/VGearsTexFile.h"
|
|
|
+#include "core/InputManager.h"
|
|
|
+#include "core/EntityManager.h"
|
|
|
+#include "core/EntityModel.h"
|
|
|
+#include "core/DialogsManager.h"
|
|
|
+#include "core/ScriptManager.h"
|
|
|
#include "data/worldmap/WorldmapFile.h"
|
|
|
#include "data/worldmap/WorldmapFileManager.h"
|
|
|
#include "data/worldmap/WorldmapFileSerializer.h"
|
|
|
#include "core/CameraManager.h"
|
|
|
-#include "data/worldmap/TxzFile.h"
|
|
|
-#include "data/worldmap/TxzFileSerializer.h"
|
|
|
|
|
|
/**
|
|
|
* World map module singleton.
|
|
|
@@ -364,80 +383,113 @@ namespace VGears {
|
|
|
{281, "wtrk", 32, 64, 64, 96}
|
|
|
};
|
|
|
|
|
|
- static const char* WORLD_RESOURCE_GROUP = "FFVII_World";
|
|
|
-
|
|
|
- /**
|
|
|
- * Creates a reference texture.
|
|
|
- *
|
|
|
- * @param[out] s The texture will be loaded here.
|
|
|
- * @todo Understand and document properly.
|
|
|
- */
|
|
|
- static void CreateReferenceTextureFileInstance(VGears::TxzFileSerializer& s){
|
|
|
-
|
|
|
- /**
|
|
|
- * A test file.
|
|
|
- */
|
|
|
- class TestFile : public VGears::TxzFile{
|
|
|
-
|
|
|
- public:
|
|
|
+ static const char* WORLD_MAP_RESOURCE_GROUP = "WORLD_MAP";
|
|
|
|
|
|
- /**
|
|
|
- * Constructor.
|
|
|
- *
|
|
|
- * Generates a null TXZ file.
|
|
|
- */
|
|
|
- TestFile() : VGears::TxzFile(nullptr, "", 0, "") {}
|
|
|
- };
|
|
|
+ static bool IsFootWalkable(uint8 walkability_type){
|
|
|
+ using WalkType = VGears::WorldmapFileSerializer::TriangleWalkMapTypes;
|
|
|
+ switch (walkability_type){
|
|
|
+ case WalkType::GRASS:
|
|
|
+ case WalkType::FOREST:
|
|
|
+ case WalkType::SWAMP:
|
|
|
+ case WalkType::DESERT:
|
|
|
+ case WalkType::WASTELAND:
|
|
|
+ case WalkType::SNOW:
|
|
|
+ case WalkType::RIVERSIDE:
|
|
|
+ case WalkType::CLIFF:
|
|
|
+ case WalkType::COREL_BRIDGE:
|
|
|
+ case WalkType::WUTAI_BRIDGE:
|
|
|
+ case WalkType::HILL_SIDE:
|
|
|
+ case WalkType::BEACH:
|
|
|
+ case WalkType::CANYON:
|
|
|
+ case WalkType::MOUNTAIN_PASS:
|
|
|
+ case WalkType::GOLD_SAUCER_DESERT:
|
|
|
+ case WalkType::JUNGLE:
|
|
|
+ case WalkType::NORTHERND_CAVE:
|
|
|
+ case WalkType::GOLD_SAUCER_DESERT_BORDER:
|
|
|
+ case WalkType::BRIDGE_HEAD:
|
|
|
+ case WalkType::BACK_ENTRANCE:
|
|
|
+ return true;
|
|
|
+ default:
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- // TODO: Get correct path
|
|
|
- // TODO: What is this file? Where does it come from?
|
|
|
- const char* file_name = "data/wm/wm0.txz";
|
|
|
- std::ifstream ifs(file_name, std::ifstream::binary);
|
|
|
- Ogre::DataStreamPtr stream(OGRE_NEW Ogre::FileStreamDataStream(&ifs, false));
|
|
|
- // Ogre::LogManager log;
|
|
|
- TestFile f;
|
|
|
- s.Import(stream, f);
|
|
|
+ static const std::map<Ogre::String, uint8>& GetWorldMapTerrainTypes(){
|
|
|
+ using WalkType = VGears::WorldmapFileSerializer::TriangleWalkMapTypes;
|
|
|
+ static const std::map<Ogre::String, uint8> terrain_types = {
|
|
|
+ {"GRASS", static_cast<uint8>(WalkType::GRASS)},
|
|
|
+ {"FOREST", static_cast<uint8>(WalkType::FOREST)},
|
|
|
+ {"MOUNTAIN", static_cast<uint8>(WalkType::MOUNTAIN)},
|
|
|
+ {"SEA_DEEP_WATER", static_cast<uint8>(WalkType::SEA_DEEP_WATER)},
|
|
|
+ {"RIVER_CROSSING", static_cast<uint8>(WalkType::RIVER_CROSSING)},
|
|
|
+ {"RIVER", static_cast<uint8>(WalkType::RIVER)},
|
|
|
+ {"WATER", static_cast<uint8>(WalkType::WATER)},
|
|
|
+ {"SWAMP", static_cast<uint8>(WalkType::SWAMP)},
|
|
|
+ {"DESERT", static_cast<uint8>(WalkType::DESERT)},
|
|
|
+ {"WASTELAND", static_cast<uint8>(WalkType::WASTELAND)},
|
|
|
+ {"SNOW", static_cast<uint8>(WalkType::SNOW)},
|
|
|
+ {"RIVERSIDE", static_cast<uint8>(WalkType::RIVERSIDE)},
|
|
|
+ {"CLIFF", static_cast<uint8>(WalkType::CLIFF)},
|
|
|
+ {"COREL_BRIDGE", static_cast<uint8>(WalkType::COREL_BRIDGE)},
|
|
|
+ {"WUTAI_BRIDGE", static_cast<uint8>(WalkType::WUTAI_BRIDGE)},
|
|
|
+ {"UNUSED_1", static_cast<uint8>(WalkType::UNUSED_1)},
|
|
|
+ {"HILL_SIDE", static_cast<uint8>(WalkType::HILL_SIDE)},
|
|
|
+ {"BEACH", static_cast<uint8>(WalkType::BEACH)},
|
|
|
+ {"SUBMARINE_PEN", static_cast<uint8>(WalkType::SUBMARINE_PEN)},
|
|
|
+ {"CANYON", static_cast<uint8>(WalkType::CANYON)},
|
|
|
+ {"MOUNTAIN_PASS", static_cast<uint8>(WalkType::MOUNTAIN_PASS)},
|
|
|
+ {"UNKNOWN_1", static_cast<uint8>(WalkType::UNKNOWN_1)},
|
|
|
+ {"WATERFALL", static_cast<uint8>(WalkType::WATERFALL)},
|
|
|
+ {"UNUSED_2", static_cast<uint8>(WalkType::UNUSED_2)},
|
|
|
+ {"GOLD_SAUCER_DESERT", static_cast<uint8>(WalkType::GOLD_SAUCER_DESERT)},
|
|
|
+ {"JUNGLE", static_cast<uint8>(WalkType::JUNGLE)},
|
|
|
+ {"SEA", static_cast<uint8>(WalkType::SEA)},
|
|
|
+ {"NORTHERND_CAVE", static_cast<uint8>(WalkType::NORTHERND_CAVE)},
|
|
|
+ {"GOLD_SAUCER_DESERT_BORDER", static_cast<uint8>(WalkType::GOLD_SAUCER_DESERT_BORDER)},
|
|
|
+ {"BRIDGE_HEAD", static_cast<uint8>(WalkType::BRIDGE_HEAD)},
|
|
|
+ {"BACK_ENTRANCE", static_cast<uint8>(WalkType::BACK_ENTRANCE)},
|
|
|
+ {"UNUSED_3", static_cast<uint8>(WalkType::UNUSED_3)}
|
|
|
+ };
|
|
|
+ return terrain_types;
|
|
|
}
|
|
|
|
|
|
+ static Ogre::String GetWorldMapTerrainName(const uint8 walkability_type){
|
|
|
+ for (const auto& terrain : GetWorldMapTerrainTypes()){
|
|
|
+ if (terrain.second == walkability_type) return terrain.first;
|
|
|
+ }
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
- * Creates a reference file.
|
|
|
+ * Loads and parses the installed WM0 map.
|
|
|
*
|
|
|
- * @param[in,out] callBack The callBack function.
|
|
|
- * @todo Understand and document properly.
|
|
|
+ * @return The map blocks.
|
|
|
*/
|
|
|
- static void CreateReferenceFileInstance(
|
|
|
- std::function<void(Ogre::DataStreamPtr& stream, VGears::WorldmapFile& file)> callBack
|
|
|
- ){
|
|
|
+ static std::vector<VGears::WorldmapFileSerializer::Block> LoadWorldMap(){
|
|
|
/**
|
|
|
- * A test file.
|
|
|
+ * The serializer retains its parsed blocks; the resource argument is
|
|
|
+ * currently unused by the MAP serializer.
|
|
|
*/
|
|
|
class TestFile : public VGears::WorldmapFile{
|
|
|
|
|
|
public:
|
|
|
|
|
|
- /**
|
|
|
- * Constructor.
|
|
|
- *
|
|
|
- * Generates a null WM file.
|
|
|
- */
|
|
|
TestFile() : VGears::WorldmapFile(nullptr, "", 0, "") {}
|
|
|
};
|
|
|
- const char* file_name = "data/wm/WM0.MAP";
|
|
|
- std::ifstream ifs(file_name, std::ifstream::binary);
|
|
|
- if (!ifs.is_open()){
|
|
|
- LogManager::getSingleton().logMessage("ERROR: Failed to open data/wm/WM0.MAP");
|
|
|
- return;
|
|
|
- }
|
|
|
- {
|
|
|
- // BOOST_REQUIRE(ifs.is_open());
|
|
|
- Ogre::DataStreamPtr stream(OGRE_NEW Ogre::FileStreamDataStream(&ifs, false));
|
|
|
- // BOOST_REQUIRE(stream->isReadable());
|
|
|
- //Ogre::LogManager log;
|
|
|
- TestFile f;
|
|
|
- // log.createLog("Default Log", true, true, true);
|
|
|
- callBack(stream, f);
|
|
|
- // log.destroyLog("Default Log");
|
|
|
- }
|
|
|
+
|
|
|
+ Ogre::DataStreamPtr stream
|
|
|
+ = Ogre::ResourceGroupManager::getSingleton().openResource(
|
|
|
+ "WM0.MAP", WORLD_MAP_RESOURCE_GROUP, nullptr, true
|
|
|
+ );
|
|
|
+ TestFile file;
|
|
|
+ VGears::WorldmapFileSerializer serializer;
|
|
|
+ serializer.ImportWorldmapFile(stream, file);
|
|
|
+
|
|
|
+ Ogre::LogManager::getSingleton().logMessage(
|
|
|
+ "Loaded WM0.MAP with " + std::to_string(serializer.blocks.size())
|
|
|
+ + " blocks."
|
|
|
+ );
|
|
|
+ return serializer.blocks;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -451,43 +503,6 @@ namespace VGears {
|
|
|
return (float(coord) / 4096.0f);
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Tests something.
|
|
|
- *
|
|
|
- * @param[in] data The test data.
|
|
|
- * @todo Understand and document properly.
|
|
|
- */
|
|
|
- void Test(const std::vector<std::vector<TxzFileSerializer::Rgba>>& data){
|
|
|
- //Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().load(
|
|
|
- // "clf_l.png.png",
|
|
|
- // Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME
|
|
|
- //);
|
|
|
-
|
|
|
- // World resource group
|
|
|
- Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().createManual(
|
|
|
- "BackgroundTex", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
|
|
|
- Ogre::TEX_TYPE_2D, 640, 256, 0, Ogre::PF_B8G8R8, Ogre:: TU_DYNAMIC
|
|
|
- );
|
|
|
- Ogre::MaterialPtr material = Ogre::MaterialManager::getSingleton().create(
|
|
|
- "BackgroundMat", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME
|
|
|
- );
|
|
|
- material->getTechnique(0)->getPass(0)->createTextureUnitState("BackgroundTex");
|
|
|
- //material->getTechnique(0)->getPass(0)->setSceneBlending(Ogre::SBT_TRANSPARENT_COLOUR);
|
|
|
- Ogre::HardwarePixelBufferSharedPtr pixel_buffer = texture->getBuffer();
|
|
|
- pixel_buffer->lock(Ogre::HardwareBuffer::HBL_DISCARD);
|
|
|
- const Ogre::PixelBox& pixelBox = pixel_buffer->getCurrentLock();
|
|
|
- Ogre::uint8* dest = static_cast<Ogre::uint8*>(pixelBox.data);
|
|
|
- for (size_t i = 0; i < 256; i ++){
|
|
|
- for(size_t j = 0; j < 640; j ++){
|
|
|
- *dest ++ = data[i][j].b;
|
|
|
- *dest ++ = data[i][j].g;
|
|
|
- *dest ++ = data[i][j].r;
|
|
|
- *dest ++ = 0;
|
|
|
- }
|
|
|
- }
|
|
|
- pixel_buffer->unlock();
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* Converts a coordinate to a texture U component coordinate.
|
|
|
*
|
|
|
@@ -496,7 +511,7 @@ namespace VGears {
|
|
|
* @return Texture coordinate U component.
|
|
|
* @todo Understand and document properly.
|
|
|
*/
|
|
|
- float ToTextureCoordU(uint8 coord, uint16 id){
|
|
|
+ static float ToTextureCoordU(uint8 coord, uint16 id){
|
|
|
return (float(coord) - float(textures[id].u)) / float(textures[id].width);
|
|
|
//return (float(textures[id].u) + float(coord)) / 256.0f;
|
|
|
}
|
|
|
@@ -509,166 +524,737 @@ namespace VGears {
|
|
|
* @return Texture coordinate V component.
|
|
|
* @todo Understand and document properly.
|
|
|
*/
|
|
|
- float ToTextureCoordV(uint8 coord, uint16 id){
|
|
|
+ static float ToTextureCoordV(uint8 coord, uint16 id){
|
|
|
return (float(coord) - float(textures[id].v)) / float(textures[id].height);
|
|
|
//return (float(textures[id].v) + float(coord)) / 640.0f;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * The list of loaded textures.
|
|
|
- */
|
|
|
- std::vector<TexturePtr> loaded_textures;
|
|
|
+ struct ImageDataDeleter{
|
|
|
|
|
|
- /**
|
|
|
- * The list of materials.
|
|
|
- */
|
|
|
- std::vector<MaterialPtr> materials;
|
|
|
+ void operator()(Ogre::ImageCodec::ImageData* image_data) const{
|
|
|
+ OGRE_DELETE image_data;
|
|
|
+ }
|
|
|
+ };
|
|
|
|
|
|
- /**
|
|
|
- * Map of texture IDs.
|
|
|
- */
|
|
|
- std::map<int, ManualObject*> texture_ids;
|
|
|
+ static TexturePtr LoadWorldMapTexture(uint16 texture_id){
|
|
|
+ const std::string texture_name
|
|
|
+ = std::string(textures[texture_id].name) + ".tex";
|
|
|
+ Ogre::DataStreamPtr stream
|
|
|
+ = ResourceGroupManager::getSingleton().openResource(
|
|
|
+ texture_name, WORLD_MAP_RESOURCE_GROUP, nullptr, true
|
|
|
+ );
|
|
|
+ VGears::TexFile tex_file;
|
|
|
+ tex_file.Read(stream);
|
|
|
|
|
|
- /**
|
|
|
- * Loads the map textures.
|
|
|
- */
|
|
|
- static void LoadTextures(){
|
|
|
- if (loaded_textures.empty()){
|
|
|
- for (int i = 0; i < sizeof(textures) / sizeof(textures[0]); i ++){
|
|
|
- std::string texture_name = std::string(textures[i].name) + ".tex";
|
|
|
- ResourceGroupManager::getSingleton().declareResource(
|
|
|
- texture_name.c_str(), "Texture", "TEST"
|
|
|
- );
|
|
|
- TexturePtr tmp = Ogre::TextureManager::getSingleton().load(
|
|
|
- texture_name.c_str(), "TEST", TEX_TYPE_2D, 1, 1.0f
|
|
|
- );
|
|
|
- loaded_textures.emplace_back(tmp);
|
|
|
- Ogre::MaterialPtr material = Ogre::MaterialManager::getSingleton().create(
|
|
|
- textures[i].name, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME
|
|
|
- );
|
|
|
- material->getTechnique(0)->getPass(0)->createTextureUnitState(texture_name.c_str());
|
|
|
- material->getTechnique(0)->getPass(0)->setTextureFiltering(TFO_NONE);
|
|
|
- materials.emplace_back(material);
|
|
|
- }
|
|
|
+ Ogre::MemoryDataStreamPtr pixel_data(tex_file.GetPixelData());
|
|
|
+ std::unique_ptr<Ogre::ImageCodec::ImageData, ImageDataDeleter> image_data(
|
|
|
+ tex_file.GetImageData()
|
|
|
+ );
|
|
|
+ TexturePtr texture = Ogre::TextureManager::getSingleton().createManual(
|
|
|
+ std::string("WorldMapTex_") + textures[texture_id].name,
|
|
|
+ ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D,
|
|
|
+ image_data->width, image_data->height, 0, image_data->format, TU_STATIC
|
|
|
+ );
|
|
|
+ texture->getBuffer()->blitFromMemory(Ogre::PixelBox(
|
|
|
+ image_data->width, image_data->height, 1, image_data->format,
|
|
|
+ pixel_data->getPtr()
|
|
|
+ ));
|
|
|
+ return texture;
|
|
|
+ }
|
|
|
+
|
|
|
+ static MaterialPtr LoadTextureMaterial(uint16 texture_id){
|
|
|
+ TexturePtr texture = LoadWorldMapTexture(texture_id);
|
|
|
+ const std::string material_name
|
|
|
+ = std::string("WorldMap_") + textures[texture_id].name;
|
|
|
+ MaterialPtr material
|
|
|
+ = Ogre::MaterialManager::getSingleton().getByName(material_name);
|
|
|
+ if (material.isNull()){
|
|
|
+ material = Ogre::MaterialManager::getSingleton().create(
|
|
|
+ material_name, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME
|
|
|
+ );
|
|
|
+ Pass* pass = material->getTechnique(0)->getPass(0);
|
|
|
+ pass->createTextureUnitState(texture->getName());
|
|
|
+ pass->setTextureFiltering(TFO_NONE);
|
|
|
+ pass->setLightingEnabled(false);
|
|
|
}
|
|
|
+ return material;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Creates a test map.
|
|
|
- *
|
|
|
- * @param[in] scene_manager The scene manager.
|
|
|
- */
|
|
|
- void CreateTestMap(SceneManager* scene_manager){
|
|
|
- std::vector<VGears::WorldmapFileSerializer::Block> blocks;
|
|
|
- CreateReferenceFileInstance([&](Ogre::DataStreamPtr& stream, VGears::WorldmapFile& file){
|
|
|
- VGears::WorldmapFileSerializer s;
|
|
|
- s.ImportWorldmapFile(stream, file);
|
|
|
- blocks = s.blocks;
|
|
|
- });
|
|
|
-
|
|
|
- if (blocks.empty()) return; // Load failed
|
|
|
- /*
|
|
|
- // TODO: Get PSX textures working
|
|
|
- VGears::TxzFileSerializer s;
|
|
|
- createReferenceTextureFileInstance(s);
|
|
|
-
|
|
|
- std::vector<std::vector<TxzFileSerializer::rgba>> data
|
|
|
- = s.GetWorldmapTexture(19);
|
|
|
- test(data);
|
|
|
- */
|
|
|
- LoadTextures();
|
|
|
- int c = 0;
|
|
|
- const auto SPACE = From16BitFixedPoint(8182);
|
|
|
- size_t block_count = blocks.size();
|
|
|
- int map_x = 0;
|
|
|
- int map_y = 0;
|
|
|
- for (unsigned int k = 0; k < block_count; k ++){
|
|
|
- // 7x9 size.
|
|
|
- VGears::WorldmapFileSerializer::Block& block = blocks[k];
|
|
|
- int block_x = 0;
|
|
|
- int block_y = 0;
|
|
|
- for (size_t i = 0; i < block.meshes.size(); i ++){
|
|
|
- VGears::WorldmapFileSerializer::BlockPart& part = block.meshes[i];
|
|
|
- for (size_t j = 0; j < part.triangles.size(); j ++){
|
|
|
- VGears::WorldmapFileSerializer::BlockTriangle& tri
|
|
|
- = part.triangles.at(j);
|
|
|
+ WorldmapModule::WorldmapModule():
|
|
|
+ scene_manager_(nullptr),
|
|
|
+ world_map_node_(nullptr),
|
|
|
+ player_entity_(nullptr),
|
|
|
+ highwind_entity_(nullptr),
|
|
|
+ buggy_entity_(nullptr),
|
|
|
+ chocobo_entity_(nullptr),
|
|
|
+ submarine_entity_(nullptr),
|
|
|
+ tiny_bronco_entity_(nullptr),
|
|
|
+ current_vehicle_(VehicleType::ON_FOOT),
|
|
|
+ player_height_offset_(0),
|
|
|
+ active_function_mesh_index_(-1),
|
|
|
+ active_function_id_(-1),
|
|
|
+ initialized_(false),
|
|
|
+ frame_listener_attached_(false),
|
|
|
+ cancel_was_pressed_(false)
|
|
|
+ {}
|
|
|
+
|
|
|
+ WorldmapModule::~WorldmapModule(){
|
|
|
+ if (frame_listener_attached_) Root::getSingleton().removeFrameListener(this);
|
|
|
+ if (scene_manager_ != nullptr && world_map_node_ != nullptr){
|
|
|
+ scene_manager_->getRootSceneNode()->removeAndDestroyChild("WorldMapTerrain");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void WorldmapModule::CreateWorldMap(SceneManager* scene_manager){
|
|
|
+ scene_manager_ = scene_manager;
|
|
|
+ world_map_node_ = scene_manager_->getRootSceneNode()
|
|
|
+ ->createChildSceneNode("WorldMapTerrain");
|
|
|
+ const std::vector<VGears::WorldmapFileSerializer::Block> blocks
|
|
|
+ = LoadWorldMap();
|
|
|
+ const size_t texture_count = sizeof(textures) / sizeof(textures[0]);
|
|
|
+ const size_t base_block_count = std::min(blocks.size(), size_t(63));
|
|
|
+ const float space = From16BitFixedPoint(8182);
|
|
|
+ std::map<uint16, ManualObject*> texture_objects;
|
|
|
+ std::set<uint16> unavailable_texture_ids;
|
|
|
+ unsigned int manual_index = 0;
|
|
|
+
|
|
|
+ for (size_t block_index = 0; block_index < base_block_count; ++block_index){
|
|
|
+ const VGears::WorldmapFileSerializer::Block& block = blocks[block_index];
|
|
|
+ const size_t map_x = block_index % 9;
|
|
|
+ const size_t map_y = block_index / 9;
|
|
|
+ for (size_t mesh_index = 0; mesh_index < block.meshes.size(); ++mesh_index){
|
|
|
+ const VGears::WorldmapFileSerializer::BlockPart& part
|
|
|
+ = block.meshes[mesh_index];
|
|
|
+ const size_t mesh_x = mesh_index % 4;
|
|
|
+ const size_t mesh_y = mesh_index / 4;
|
|
|
+ const float pos_x = (mesh_x * space) + (map_x * space * 4);
|
|
|
+ const float pos_z = (mesh_y * space) + (map_y * space * 4);
|
|
|
+
|
|
|
+ for (const VGears::WorldmapFileSerializer::BlockTriangle& triangle
|
|
|
+ : part.triangles){
|
|
|
+ const VGears::WorldmapFileSerializer::Vertex& vertex_0
|
|
|
+ = part.vertices.at(triangle.vertex_0_index);
|
|
|
+ const VGears::WorldmapFileSerializer::Vertex& vertex_1
|
|
|
+ = part.vertices.at(triangle.vertex_1_index);
|
|
|
+ const VGears::WorldmapFileSerializer::Vertex& vertex_2
|
|
|
+ = part.vertices.at(triangle.vertex_2_index);
|
|
|
+ TerrainTriangle terrain_triangle;
|
|
|
+ terrain_triangle.vertices[0] = Ogre::Vector3(
|
|
|
+ From16BitFixedPoint(vertex_0.x) + pos_x,
|
|
|
+ From16BitFixedPoint(vertex_0.y),
|
|
|
+ From16BitFixedPoint(vertex_0.z) + pos_z
|
|
|
+ );
|
|
|
+ terrain_triangle.vertices[1] = Ogre::Vector3(
|
|
|
+ From16BitFixedPoint(vertex_1.x) + pos_x,
|
|
|
+ From16BitFixedPoint(vertex_1.y),
|
|
|
+ From16BitFixedPoint(vertex_1.z) + pos_z
|
|
|
+ );
|
|
|
+ terrain_triangle.vertices[2] = Ogre::Vector3(
|
|
|
+ From16BitFixedPoint(vertex_2.x) + pos_x,
|
|
|
+ From16BitFixedPoint(vertex_2.y),
|
|
|
+ From16BitFixedPoint(vertex_2.z) + pos_z
|
|
|
+ );
|
|
|
+ terrain_triangle.walkability_type = triangle.walkability_info_;
|
|
|
+ terrain_triangle.function_id = triangle.function_id;
|
|
|
+ terrain_triangle.mesh_x = static_cast<int>(map_x * 4 + mesh_x);
|
|
|
+ terrain_triangle.mesh_y = static_cast<int>(map_y * 4 + mesh_y);
|
|
|
+ terrain_triangles_.push_back(terrain_triangle);
|
|
|
+
|
|
|
+ if (unavailable_texture_ids.find(triangle.TextureInfo)
|
|
|
+ != unavailable_texture_ids.end()){
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (triangle.TextureInfo >= texture_count){
|
|
|
+ if (unavailable_texture_ids.insert(triangle.TextureInfo).second){
|
|
|
+ LogManager::getSingleton().logMessage(
|
|
|
+ "Skipping world-map texture ID "
|
|
|
+ + std::to_string(triangle.TextureInfo)
|
|
|
+ + ": no PC texture mapping is available."
|
|
|
+ );
|
|
|
+ }
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
ManualObject* manual = nullptr;
|
|
|
- auto it = texture_ids.find(tri.TextureInfo);
|
|
|
- if (it == texture_ids.end()){
|
|
|
+ auto object = texture_objects.find(triangle.TextureInfo);
|
|
|
+ if (object == texture_objects.end()){
|
|
|
+ MaterialPtr material;
|
|
|
+ try{
|
|
|
+ material = LoadTextureMaterial(triangle.TextureInfo);
|
|
|
+ }
|
|
|
+ catch (const Ogre::Exception& exception){
|
|
|
+ unavailable_texture_ids.insert(triangle.TextureInfo);
|
|
|
+ LogManager::getSingleton().logMessage(
|
|
|
+ "Failed to load world-map texture "
|
|
|
+ + std::string(textures[triangle.TextureInfo].name)
|
|
|
+ + ": " + exception.getDescription()
|
|
|
+ );
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
manual = scene_manager->createManualObject(
|
|
|
- ("zzz_manual" + std::to_string(c++)).c_str()
|
|
|
- );
|
|
|
- manual->begin(
|
|
|
- textures[tri.TextureInfo].name, RenderOperation::OT_TRIANGLE_LIST
|
|
|
+ "world_map_surface_" + std::to_string(++manual_index)
|
|
|
);
|
|
|
- manual->setDebugDisplayEnabled(true);
|
|
|
+ manual->begin(material->getName(), RenderOperation::OT_TRIANGLE_LIST);
|
|
|
+ manual->setDebugDisplayEnabled(false);
|
|
|
manual->setDynamic(false);
|
|
|
- texture_ids[tri.TextureInfo] = manual;
|
|
|
+ texture_objects[triangle.TextureInfo] = manual;
|
|
|
}
|
|
|
- else manual = it->second;
|
|
|
- const float pos_x = (block_x * SPACE) + (map_x * SPACE * 4);
|
|
|
- const float pos_y = (block_y * SPACE) + (map_y * SPACE * 4);
|
|
|
+ else manual = object->second;
|
|
|
|
|
|
- // v1
|
|
|
- VGears::WorldmapFileSerializer::Vertex v1
|
|
|
- = part.vertices.at(tri.vertex_2_index);
|
|
|
manual->position(
|
|
|
- From16BitFixedPoint(v1.x) + pos_x,
|
|
|
- From16BitFixedPoint(v1.y),
|
|
|
- From16BitFixedPoint(v1.z) + pos_y);
|
|
|
+ terrain_triangle.vertices[2].x,
|
|
|
+ terrain_triangle.vertices[2].y,
|
|
|
+ terrain_triangle.vertices[2].z
|
|
|
+ );
|
|
|
manual->textureCoord(
|
|
|
- ToTextureCoordU(tri.vertex_2_u, tri.TextureInfo),
|
|
|
- ToTextureCoordV(tri.vertex_2_v, tri.TextureInfo)
|
|
|
+ ToTextureCoordU(triangle.vertex_2_u, triangle.TextureInfo),
|
|
|
+ ToTextureCoordV(triangle.vertex_2_v, triangle.TextureInfo)
|
|
|
);
|
|
|
|
|
|
- // v2
|
|
|
- v1 = part.vertices.at(tri.vertex_1_index);
|
|
|
manual->position(
|
|
|
- From16BitFixedPoint(v1.x) + pos_x,
|
|
|
- From16BitFixedPoint(v1.y),
|
|
|
- From16BitFixedPoint(v1.z) + pos_y
|
|
|
+ terrain_triangle.vertices[1].x,
|
|
|
+ terrain_triangle.vertices[1].y,
|
|
|
+ terrain_triangle.vertices[1].z
|
|
|
);
|
|
|
manual->textureCoord(
|
|
|
- ToTextureCoordU(tri.vertex_1_u, tri.TextureInfo),
|
|
|
- ToTextureCoordV(tri.vertex_1_v, tri.TextureInfo)
|
|
|
+ ToTextureCoordU(triangle.vertex_1_u, triangle.TextureInfo),
|
|
|
+ ToTextureCoordV(triangle.vertex_1_v, triangle.TextureInfo)
|
|
|
);
|
|
|
|
|
|
- // v3
|
|
|
- v1 = part.vertices.at(tri.vertex_0_index);
|
|
|
manual->position(
|
|
|
- From16BitFixedPoint(v1.x) + pos_x,
|
|
|
- From16BitFixedPoint(v1.y),
|
|
|
- From16BitFixedPoint(v1.z) + pos_y
|
|
|
+ terrain_triangle.vertices[0].x,
|
|
|
+ terrain_triangle.vertices[0].y,
|
|
|
+ terrain_triangle.vertices[0].z
|
|
|
);
|
|
|
manual->textureCoord(
|
|
|
- ToTextureCoordU(tri.vertex_0_u, tri.TextureInfo),
|
|
|
- ToTextureCoordV(tri.vertex_0_v, tri.TextureInfo)
|
|
|
+ ToTextureCoordU(triangle.vertex_0_u, triangle.TextureInfo),
|
|
|
+ ToTextureCoordV(triangle.vertex_0_v, triangle.TextureInfo)
|
|
|
);
|
|
|
-
|
|
|
- }
|
|
|
- block_x ++;
|
|
|
- if (block_x >= 4){
|
|
|
- block_x = 0;
|
|
|
- block_y++;
|
|
|
}
|
|
|
}
|
|
|
- map_x ++;
|
|
|
- if (map_x >= 9){
|
|
|
- map_x = 0;
|
|
|
- map_y++;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (const auto& texture_object : texture_objects){
|
|
|
+ texture_object.second->end();
|
|
|
+ world_map_node_->attachObject(texture_object.second);
|
|
|
+ }
|
|
|
+
|
|
|
+ const float map_center_x = space * 18.0f;
|
|
|
+ const float map_center_z = space * 14.0f;
|
|
|
+ Ogre::Camera* camera = CameraManager::getSingleton().GetCurrentCamera();
|
|
|
+ camera->setPosition(Ogre::Vector3(map_center_x, 80.0f, map_center_z + 90.0f));
|
|
|
+ camera->lookAt(Ogre::Vector3(map_center_x, 0.0f, map_center_z));
|
|
|
+ }
|
|
|
+
|
|
|
+ void WorldmapModule::CreatePlayer(){
|
|
|
+ const Ogre::Vector3 requested_spawn(42.0f, 0.0f, 36.5f);
|
|
|
+ Ogre::Vector3 spawn_position;
|
|
|
+ if (!FindWalkableSurface(requested_spawn, spawn_position, true)){
|
|
|
+ LogManager::getSingleton().logMessage(
|
|
|
+ "WM0 has no GRASS surface at Cloud's requested spawn (42, 36.5)."
|
|
|
+ );
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ LogManager::getSingleton().logMessage(
|
|
|
+ "Spawning Cloud at "
|
|
|
+ + Ogre::StringConverter::toString(spawn_position.x) + ", "
|
|
|
+ + Ogre::StringConverter::toString(spawn_position.z)
|
|
|
+ );
|
|
|
+
|
|
|
+ player_entity_ = EntityManager::getSingleton().AddWorldMapEntity(
|
|
|
+ "WorldMapCloud", "cloud.mesh", spawn_position, Ogre::Degree(0),
|
|
|
+ Ogre::Vector3::UNIT_SCALE, "FIELD_MODELS"
|
|
|
+ );
|
|
|
+ if (player_entity_ == nullptr) return;
|
|
|
+
|
|
|
+ const Ogre::AxisAlignedBox& bounds = player_entity_->GetModel()->getBoundingBox();
|
|
|
+ const Ogre::Vector3 model_size = bounds.getSize();
|
|
|
+ const Ogre::Real model_height = model_size.y;
|
|
|
+ if (model_height > 0){
|
|
|
+ const Ogre::Real scale = From16BitFixedPoint(8182) * 0.45f * 0.25f * 0.25f
|
|
|
+ / model_height;
|
|
|
+ player_entity_->setScale(Ogre::Vector3::UNIT_SCALE * scale);
|
|
|
+ player_entity_->SetHeight(model_height * scale);
|
|
|
+ player_entity_->SetSolidRadius(
|
|
|
+ std::max(model_size.x, model_size.z) * scale * 0.5f
|
|
|
+ );
|
|
|
+ player_entity_->SetCollisionDebugOrientation(
|
|
|
+ Ogre::Quaternion(Ogre::Degree(-90), Ogre::Vector3::UNIT_X)
|
|
|
+ );
|
|
|
+ player_height_offset_ = -bounds.getMinimum().y * scale;
|
|
|
+ spawn_position.y += player_height_offset_;
|
|
|
+ player_entity_->SetPosition(spawn_position);
|
|
|
+ Ogre::AnimationStateSet* animations
|
|
|
+ = player_entity_->GetModel()->getAllAnimationStates();
|
|
|
+ if (animations == nullptr || !animations->hasAnimationState("Walk")){
|
|
|
+ LogManager::getSingleton().logMessage(
|
|
|
+ "Cloud model has no Walk animation state."
|
|
|
+ );
|
|
|
+ }
|
|
|
+ EntityManager::getSingleton().ScriptSetPlayerEntity("WorldMapCloud");
|
|
|
+ player_entity_->PlayAnimationContinue("Idle");
|
|
|
+ CreateCompanionModels();
|
|
|
+ UpdateCamera(0, true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void WorldmapModule::AddWorldModel(
|
|
|
+ const Ogre::String& name, const Ogre::String& mesh_name,
|
|
|
+ const Ogre::Vector3& desired_position, Ogre::Real target_size,
|
|
|
+ const Ogre::Quaternion& orientation, Ogre::Real collision_radius_scale,
|
|
|
+ bool talkable
|
|
|
+ ){
|
|
|
+ Ogre::Vector3 surface_position;
|
|
|
+ if (!FindWalkableSurface(desired_position, surface_position, false, false)){
|
|
|
+ LogManager::getSingleton().logMessage(
|
|
|
+ "No terrain surface found for world-map model '" + name + "'."
|
|
|
+ );
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ EntityModel* entity = nullptr;
|
|
|
+ try{
|
|
|
+ entity = EntityManager::getSingleton().AddWorldMapEntity(
|
|
|
+ "WorldMap" + name, mesh_name, surface_position,
|
|
|
+ Ogre::Degree(0), Ogre::Vector3::UNIT_SCALE, "WORLD_MODELS"
|
|
|
+ );
|
|
|
+ }
|
|
|
+ catch (const Ogre::Exception& exception){
|
|
|
+ LogManager::getSingleton().logMessage(
|
|
|
+ "Failed to load world-map model '" + mesh_name + "': "
|
|
|
+ + exception.getDescription()
|
|
|
+ );
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (entity == nullptr) return;
|
|
|
+
|
|
|
+ const Ogre::Vector3 bounds_size = entity->GetModel()->getBoundingBox().getSize();
|
|
|
+ const Ogre::Real largest_dimension = std::max(
|
|
|
+ bounds_size.x, std::max(bounds_size.y, bounds_size.z)
|
|
|
+ );
|
|
|
+ if (largest_dimension <= 0){
|
|
|
+ LogManager::getSingleton().logMessage(
|
|
|
+ "World-map model '" + mesh_name + "' has empty bounds."
|
|
|
+ );
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const Ogre::Real model_scale = target_size / largest_dimension;
|
|
|
+ const Ogre::Vector3 oriented_x = orientation * Ogre::Vector3::UNIT_X;
|
|
|
+ const Ogre::Vector3 oriented_y = orientation * Ogre::Vector3::UNIT_Y;
|
|
|
+ const Ogre::Vector3 oriented_z = orientation * Ogre::Vector3::UNIT_Z;
|
|
|
+ const Ogre::Real world_width =
|
|
|
+ Ogre::Math::Abs(oriented_x.x) * bounds_size.x
|
|
|
+ + Ogre::Math::Abs(oriented_y.x) * bounds_size.y
|
|
|
+ + Ogre::Math::Abs(oriented_z.x) * bounds_size.z;
|
|
|
+ const Ogre::Real world_height =
|
|
|
+ Ogre::Math::Abs(oriented_x.y) * bounds_size.x
|
|
|
+ + Ogre::Math::Abs(oriented_y.y) * bounds_size.y
|
|
|
+ + Ogre::Math::Abs(oriented_z.y) * bounds_size.z;
|
|
|
+ const Ogre::Real world_depth =
|
|
|
+ Ogre::Math::Abs(oriented_x.z) * bounds_size.x
|
|
|
+ + Ogre::Math::Abs(oriented_y.z) * bounds_size.y
|
|
|
+ + Ogre::Math::Abs(oriented_z.z) * bounds_size.z;
|
|
|
+ entity->setRootOrientation(orientation);
|
|
|
+ entity->setScale(Ogre::Vector3::UNIT_SCALE * model_scale);
|
|
|
+ entity->SetSolid(true);
|
|
|
+ entity->SetHeight(world_height * model_scale);
|
|
|
+ entity->SetSolidRadius(
|
|
|
+ std::max(world_width, world_depth) * model_scale * 0.5f
|
|
|
+ * collision_radius_scale
|
|
|
+ );
|
|
|
+ entity->SetTalkable(talkable);
|
|
|
+ if (talkable) entity->SetTalkRadius(entity->GetSolidRadius());
|
|
|
+ entity->SetCollisionDebugOrientation(
|
|
|
+ Ogre::Quaternion(Ogre::Degree(-90), Ogre::Vector3::UNIT_X)
|
|
|
+ );
|
|
|
+ LogManager::getSingleton().logMessage(
|
|
|
+ "Placed world-map model '" + name + "' at "
|
|
|
+ + Ogre::StringConverter::toString(surface_position.x) + ", "
|
|
|
+ + Ogre::StringConverter::toString(surface_position.z)
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ void WorldmapModule::CreateCompanionModels(){
|
|
|
+ // These models and their spawn points are now hardcoded, they mmust be
|
|
|
+ // loaded according to the story point and save game data.
|
|
|
+ const Ogre::Real space = From16BitFixedPoint(8182);
|
|
|
+ AddWorldModel(
|
|
|
+ "Chocobo", "aja.mesh",
|
|
|
+ Ogre::Vector3(42.5f, 0.0f, 37.5f),
|
|
|
+ space * 0.14f,
|
|
|
+ Ogre::Quaternion(Ogre::Degree(-90), Ogre::Vector3::UNIT_X), 1.0f, true
|
|
|
+ );
|
|
|
+ AddWorldModel(
|
|
|
+ "Highwind", "cgd.mesh",
|
|
|
+ Ogre::Vector3(43.0f, 0.0f, 36.5f),
|
|
|
+ space * 0.25f,
|
|
|
+ Ogre::Quaternion(Ogre::Degree(-90), Ogre::Vector3::UNIT_X), 0.5f, true
|
|
|
+ );
|
|
|
+ AddWorldModel(
|
|
|
+ "Buggy", "aba.mesh",
|
|
|
+ Ogre::Vector3(42.0f, 0.0f, 36.0f),
|
|
|
+ space * 0.3f, Ogre::Quaternion::IDENTITY, 0.5f, true
|
|
|
+ );
|
|
|
+ AddWorldModel(
|
|
|
+ "Submarine", "ddd.mesh",
|
|
|
+ Ogre::Vector3(41.5f, 0.0f, 36.5f),
|
|
|
+ space * 0.1125f, Ogre::Quaternion::IDENTITY, 1.0f, true
|
|
|
+ );
|
|
|
+ AddWorldModel(
|
|
|
+ "TinyBronco", "dva.mesh",
|
|
|
+ Ogre::Vector3(42.0f, 0.0f, 37.37f),
|
|
|
+ space * 0.15f, Ogre::Quaternion::IDENTITY, 0.75f, true
|
|
|
+ );
|
|
|
+ highwind_entity_ = static_cast<EntityModel*>(
|
|
|
+ EntityManager::getSingleton().GetEntity("WorldMapHighwind")
|
|
|
+ );
|
|
|
+ buggy_entity_ = static_cast<EntityModel*>(
|
|
|
+ EntityManager::getSingleton().GetEntity("WorldMapBuggy")
|
|
|
+ );
|
|
|
+ chocobo_entity_ = static_cast<EntityModel*>(
|
|
|
+ EntityManager::getSingleton().GetEntity("WorldMapChocobo")
|
|
|
+ );
|
|
|
+ submarine_entity_ = static_cast<EntityModel*>(
|
|
|
+ EntityManager::getSingleton().GetEntity("WorldMapSubmarine")
|
|
|
+ );
|
|
|
+ tiny_bronco_entity_ = static_cast<EntityModel*>(
|
|
|
+ EntityManager::getSingleton().GetEntity("WorldMapTinyBronco")
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ bool WorldmapModule::FindWalkableSurface(
|
|
|
+ const Ogre::Vector3& position, Ogre::Vector3& surface, bool grass_only,
|
|
|
+ bool require_walkable, uint8* walkability_type, size_t* triangle_index
|
|
|
+ ) const{
|
|
|
+ bool found = false;
|
|
|
+ Ogre::Real closest_height_distance = std::numeric_limits<Ogre::Real>::max();
|
|
|
+ for (size_t i = 0; i < terrain_triangles_.size(); ++i){
|
|
|
+ const TerrainTriangle& triangle = terrain_triangles_[i];
|
|
|
+ if (grass_only
|
|
|
+ && triangle.walkability_type != VGears::WorldmapFileSerializer::GRASS){
|
|
|
+ continue;
|
|
|
}
|
|
|
+ const Ogre::Vector3& a = triangle.vertices[0];
|
|
|
+ const Ogre::Vector3& b = triangle.vertices[1];
|
|
|
+ const Ogre::Vector3& c = triangle.vertices[2];
|
|
|
+ const Ogre::Real denominator
|
|
|
+ = (b.z - c.z) * (a.x - c.x) + (c.x - b.x) * (a.z - c.z);
|
|
|
+ if (Ogre::Math::Abs(denominator) < 1e-6f) continue;
|
|
|
+
|
|
|
+ const Ogre::Real weight_a
|
|
|
+ = ((b.z - c.z) * (position.x - c.x)
|
|
|
+ + (c.x - b.x) * (position.z - c.z)) / denominator;
|
|
|
+ const Ogre::Real weight_b
|
|
|
+ = ((c.z - a.z) * (position.x - c.x)
|
|
|
+ + (a.x - c.x) * (position.z - c.z)) / denominator;
|
|
|
+ const Ogre::Real weight_c = 1.0f - weight_a - weight_b;
|
|
|
+ if (weight_a < -1e-4f || weight_b < -1e-4f || weight_c < -1e-4f){
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ const Ogre::Real height
|
|
|
+ = weight_a * a.y + weight_b * b.y + weight_c * c.y;
|
|
|
+ const Ogre::Real height_distance = Ogre::Math::Abs(height - position.y);
|
|
|
+ if (height_distance >= closest_height_distance) continue;
|
|
|
+ closest_height_distance = height_distance;
|
|
|
+ found = !require_walkable || IsActiveTerrainAllowed(triangle.walkability_type);
|
|
|
+ surface = Ogre::Vector3(position.x, height, position.z);
|
|
|
+ if (walkability_type != nullptr) *walkability_type = triangle.walkability_type;
|
|
|
+ if (triangle_index != nullptr) *triangle_index = i;
|
|
|
+ }
|
|
|
+ return found;
|
|
|
+ }
|
|
|
+
|
|
|
+ WorldmapModule::VehicleType WorldmapModule::GetCurrentVehicle() const{
|
|
|
+ return current_vehicle_;
|
|
|
+ }
|
|
|
+
|
|
|
+ void WorldmapModule::ClearVehicleTerrains(){
|
|
|
+ active_vehicle_terrain_types_.clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ void WorldmapModule::AddVehicleTerrain(const char* terrain_name){
|
|
|
+ if (terrain_name == nullptr) return;
|
|
|
+ const auto terrain = GetWorldMapTerrainTypes().find(terrain_name);
|
|
|
+ if (terrain == GetWorldMapTerrainTypes().end()){
|
|
|
+ LogManager::getSingleton().logMessage(
|
|
|
+ "Unknown world-map vehicle terrain '" + Ogre::String(terrain_name) + "'."
|
|
|
+ );
|
|
|
+ return;
|
|
|
}
|
|
|
- for (auto& it : texture_ids){
|
|
|
- it.second->end();
|
|
|
- SceneNode* node = scene_manager->getRootSceneNode()->createChildSceneNode();
|
|
|
- node->attachObject(it.second);
|
|
|
- node->setPosition(0, 0, 0);
|
|
|
+ if (std::find(
|
|
|
+ active_vehicle_terrain_types_.begin(), active_vehicle_terrain_types_.end(),
|
|
|
+ terrain->second
|
|
|
+ ) == active_vehicle_terrain_types_.end()){
|
|
|
+ active_vehicle_terrain_types_.push_back(terrain->second);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ bool WorldmapModule::IsActiveTerrainAllowed(uint8 walkability_type) const{
|
|
|
+ if (current_vehicle_ == VehicleType::ON_FOOT){
|
|
|
+ return IsFootWalkable(walkability_type);
|
|
|
+ }
|
|
|
+ return std::find(
|
|
|
+ active_vehicle_terrain_types_.begin(), active_vehicle_terrain_types_.end(),
|
|
|
+ walkability_type
|
|
|
+ ) != active_vehicle_terrain_types_.end();
|
|
|
+ }
|
|
|
+
|
|
|
+ void WorldmapModule::TriggerTriangleFunction(const TerrainTriangle& triangle){
|
|
|
+ ScriptEntity* world_map_script
|
|
|
+ = ScriptManager::getSingleton().GetScriptEntityByName(
|
|
|
+ ScriptManager::SYSTEM, "WorldMap"
|
|
|
+ );
|
|
|
+ if (world_map_script == nullptr) return;
|
|
|
+
|
|
|
+ const int mesh_index = triangle.mesh_y * 36 + triangle.mesh_x;
|
|
|
+ ScriptManager::getSingleton().ScriptRequest(
|
|
|
+ world_map_script, "on_triangle_enter", 1,
|
|
|
+ std::to_string(mesh_index), std::to_string(triangle.function_id - 3), false, false
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Ogre::String WorldmapModule::GetTerrainTypeUnderEntity(const char* entity_name) const{
|
|
|
+ if (entity_name == nullptr) return "";
|
|
|
+ const ::Entity* entity = EntityManager::getSingleton().GetEntity(entity_name);
|
|
|
+ if (entity == nullptr) return "";
|
|
|
+
|
|
|
+ Ogre::Vector3 surface;
|
|
|
+ uint8 walkability_type = 0;
|
|
|
+ if (!FindWalkableSurface(entity->GetPosition(), surface, false, false, &walkability_type)){
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ return GetWorldMapTerrainName(walkability_type);
|
|
|
+ }
|
|
|
+
|
|
|
+ int WorldmapModule::GetTerrainTriangleUnderEntity(const char* entity_name) const{
|
|
|
+ if (entity_name == nullptr) return -1;
|
|
|
+ const ::Entity* entity = EntityManager::getSingleton().GetEntity(entity_name);
|
|
|
+ if (entity == nullptr) return -1;
|
|
|
+
|
|
|
+ Ogre::Vector3 surface;
|
|
|
+ size_t triangle_index = 0;
|
|
|
+ if (!FindWalkableSurface(entity->GetPosition(), surface, false, false, nullptr, &triangle_index)){
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ return static_cast<int>(triangle_index);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool WorldmapModule::UnboardVehicle(const char* vehicle_name){
|
|
|
+ if (vehicle_name == nullptr) return false;
|
|
|
+
|
|
|
+ EntityManager& entity_manager = EntityManager::getSingleton();
|
|
|
+ ::Entity* player_entity = entity_manager.ScriptGetPlayerEntity();
|
|
|
+ ::Entity* vehicle_entity = entity_manager.GetEntity(vehicle_name);
|
|
|
+ if (
|
|
|
+ player_entity == nullptr || vehicle_entity == nullptr
|
|
|
+ || player_entity != vehicle_entity || player_entity == player_entity_
|
|
|
+ ){
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ const Ogre::Vector3 vehicle_position = vehicle_entity->GetPosition();
|
|
|
+ const Ogre::Real unboard_distance
|
|
|
+ = vehicle_entity->GetSolidRadius() + player_entity_->GetSolidRadius() + 0.1f;
|
|
|
+ const Ogre::Real diagonal_distance = unboard_distance * 0.70710678f;
|
|
|
+ const Ogre::Vector3 directions[] = {
|
|
|
+ Ogre::Vector3(1, 0, 0), Ogre::Vector3(-1, 0, 0),
|
|
|
+ Ogre::Vector3(0, 0, 1), Ogre::Vector3(0, 0, -1),
|
|
|
+ Ogre::Vector3(1, 0, 1), Ogre::Vector3(-1, 0, 1),
|
|
|
+ Ogre::Vector3(1, 0, -1), Ogre::Vector3(-1, 0, -1)
|
|
|
+ };
|
|
|
+
|
|
|
+ for (size_t i = 0; i < sizeof(directions) / sizeof(directions[0]); ++i){
|
|
|
+ const Ogre::Real distance = i < 4 ? unboard_distance : diagonal_distance;
|
|
|
+ const Ogre::Vector3 requested_position
|
|
|
+ = vehicle_position + directions[i] * distance;
|
|
|
+ Ogre::Vector3 surface;
|
|
|
+ uint8 walkability_type = 0;
|
|
|
+ if (!FindWalkableSurface(
|
|
|
+ requested_position, surface, false, false, &walkability_type
|
|
|
+ ) || !IsFootWalkable(walkability_type)){
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ surface.y += player_height_offset_;
|
|
|
+ if (entity_manager.IsWorldPositionBlocked(player_entity_, surface)) continue;
|
|
|
+
|
|
|
+ player_entity_->SetPosition(surface);
|
|
|
+ vehicle_entity->SetTalkable(true);
|
|
|
+ entity_manager.ScriptSetPlayerEntity("WorldMapCloud");
|
|
|
+ ClearVehicleTerrains();
|
|
|
+ current_vehicle_ = VehicleType::ON_FOOT;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool WorldmapModule::frameStarted(const Ogre::FrameEvent& event){
|
|
|
+ if (!initialized_ || player_entity_ == nullptr) return true;
|
|
|
+
|
|
|
+ ::Entity* active_entity = EntityManager::getSingleton().ScriptGetPlayerEntity();
|
|
|
+ if (active_entity == player_entity_){
|
|
|
+ current_vehicle_ = VehicleType::ON_FOOT;
|
|
|
+ }
|
|
|
+ else if (active_entity == chocobo_entity_){
|
|
|
+ current_vehicle_ = VehicleType::CHOCOBO;
|
|
|
+ }
|
|
|
+ else if (active_entity == buggy_entity_){
|
|
|
+ current_vehicle_ = VehicleType::BUGGY;
|
|
|
+ }
|
|
|
+ else if (active_entity == highwind_entity_){
|
|
|
+ current_vehicle_ = VehicleType::HIGHWIND;
|
|
|
+ }
|
|
|
+ else if (active_entity == submarine_entity_){
|
|
|
+ current_vehicle_ = VehicleType::SUBMARINE;
|
|
|
+ }
|
|
|
+ else if (active_entity == tiny_bronco_entity_){
|
|
|
+ current_vehicle_ = VehicleType::TINY_BRONCO;
|
|
|
+ }
|
|
|
+ else{
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ const bool highwind_active = active_entity == highwind_entity_;
|
|
|
+ const bool vehicle_active = current_vehicle_ != VehicleType::ON_FOOT;
|
|
|
+ const bool cancel_pressed
|
|
|
+ = InputManager::getSingleton().IsButtonPressed(OIS::KC_ESCAPE);
|
|
|
+ if (vehicle_active && cancel_pressed && !cancel_was_pressed_){
|
|
|
+ ScriptManager::getSingleton().ScriptRequest(
|
|
|
+ ScriptManager::ENTITY, active_entity->GetName().c_str(), "unboard", 0
|
|
|
+ );
|
|
|
+ }
|
|
|
+ cancel_was_pressed_ = cancel_pressed;
|
|
|
+ EntityModel* active_model = active_entity == player_entity_ ? player_entity_
|
|
|
+ : (current_vehicle_ == VehicleType::CHOCOBO ? chocobo_entity_
|
|
|
+ : (current_vehicle_ == VehicleType::BUGGY ? buggy_entity_
|
|
|
+ : (highwind_active ? highwind_entity_
|
|
|
+ : (current_vehicle_ == VehicleType::SUBMARINE
|
|
|
+ ? submarine_entity_ : tiny_bronco_entity_))));
|
|
|
+
|
|
|
+ Ogre::Vector3 movement = Ogre::Vector3::ZERO;
|
|
|
+ if (InputManager::getSingleton().IsButtonPressed(OIS::KC_LEFT)) movement.x -= 1.0f;
|
|
|
+ if (InputManager::getSingleton().IsButtonPressed(OIS::KC_RIGHT)) movement.x += 1.0f;
|
|
|
+ if (InputManager::getSingleton().IsButtonPressed(OIS::KC_UP)) movement.z -= 1.0f;
|
|
|
+ if (InputManager::getSingleton().IsButtonPressed(OIS::KC_DOWN)) movement.z += 1.0f;
|
|
|
+ bool is_walking = false;
|
|
|
+ if (!movement.isZeroLength()){
|
|
|
+ movement.normalise();
|
|
|
+ const Ogre::Vector3 current_position = active_entity->GetPosition();
|
|
|
+ const Ogre::Real speed = From16BitFixedPoint(8182) * 0.45f
|
|
|
+ * (vehicle_active ? 2.0f : 1.0f);
|
|
|
+ Ogre::Vector3 requested_position = current_position
|
|
|
+ + movement * speed * event.timeSinceLastFrame;
|
|
|
+ Ogre::Vector3 surface;
|
|
|
+ size_t triangle_index = 0;
|
|
|
+ if (FindWalkableSurface(requested_position, surface, false, true, nullptr,
|
|
|
+ &triangle_index)){
|
|
|
+ if (highwind_active){
|
|
|
+ Ogre::Vector3 current_surface;
|
|
|
+ if (FindWalkableSurface(current_position, current_surface, false, false)){
|
|
|
+ surface.y += current_position.y - current_surface.y;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (active_entity == player_entity_){
|
|
|
+ surface.y += player_height_offset_;
|
|
|
+ }
|
|
|
+ if (!EntityManager::getSingleton().IsWorldPositionBlocked(
|
|
|
+ active_entity, surface
|
|
|
+ )){
|
|
|
+ active_entity->SetPosition(surface);
|
|
|
+ const TerrainTriangle& triangle = terrain_triangles_[triangle_index];
|
|
|
+ const int mesh_index = triangle.mesh_y * 36 + triangle.mesh_x;
|
|
|
+ if (triangle.function_id >= 3){
|
|
|
+ if (mesh_index != active_function_mesh_index_
|
|
|
+ || triangle.function_id != active_function_id_){
|
|
|
+ TriggerTriangleFunction(triangle);
|
|
|
+ }
|
|
|
+ active_function_mesh_index_ = mesh_index;
|
|
|
+ active_function_id_ = triangle.function_id;
|
|
|
+ }
|
|
|
+ else{
|
|
|
+ active_function_mesh_index_ = -1;
|
|
|
+ active_function_id_ = -1;
|
|
|
+ }
|
|
|
+ const Ogre::Vector3 model_forward(0.0f, 0.0f, 1.0f);
|
|
|
+ const Ogre::Vector3 move_direction(movement.x, 0.0f, movement.z);
|
|
|
+ const Ogre::Quaternion heading
|
|
|
+ = model_forward.getRotationTo(move_direction, Ogre::Vector3::UNIT_Y);
|
|
|
+ active_model->GetModel()->getParentSceneNode()
|
|
|
+ ->getParentSceneNode()->setOrientation(heading);
|
|
|
+ is_walking = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!vehicle_active){
|
|
|
+ player_entity_->PlayAnimationContinue(is_walking ? "Walk" : "Idle");
|
|
|
+ }
|
|
|
+
|
|
|
+ UpdateCamera(event.timeSinceLastFrame, false);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ void WorldmapModule::UpdateCamera(Ogre::Real delta_time, bool snap_to_player){
|
|
|
+ ::Entity* active_entity = EntityManager::getSingleton().ScriptGetPlayerEntity();
|
|
|
+ if (active_entity == nullptr) return;
|
|
|
+ const Ogre::Vector3 target
|
|
|
+ = active_entity->GetPosition() + Ogre::Vector3(0, active_entity->GetHeight() * 0.5f, 0);
|
|
|
+ const Ogre::Vector3 camera_offset(0.0f, 1.5f, 2.25f);
|
|
|
+ Ogre::Camera* camera = CameraManager::getSingleton().GetCurrentCamera();
|
|
|
+ const Ogre::Vector3 desired_position = target + camera_offset;
|
|
|
+ if (snap_to_player){
|
|
|
+ camera->setPosition(desired_position);
|
|
|
+ }
|
|
|
+ else{
|
|
|
+ const Ogre::Real follow_amount = std::min(1.0f, delta_time * 8.0f);
|
|
|
+ camera->setPosition(
|
|
|
+ camera->getPosition()
|
|
|
+ + (desired_position - camera->getPosition()) * follow_amount
|
|
|
+ );
|
|
|
+ }
|
|
|
+ camera->lookAt(target);
|
|
|
+ }
|
|
|
+
|
|
|
void WorldmapModule::Init(){
|
|
|
- CreateTestMap(Root::getSingleton().getSceneManager("Scene"));
|
|
|
- CameraManager::getSingleton().EnableWireFrame(true);
|
|
|
+ if (initialized_){
|
|
|
+ if (frame_listener_attached_) return;
|
|
|
+ EntityManager::getSingleton().SetWorldModule();
|
|
|
+ DialogsManager::getSingleton().SetWorldModule();
|
|
|
+ world_map_node_->setVisible(true);
|
|
|
+ CreatePlayer();
|
|
|
+ Root::getSingleton().addFrameListener(this);
|
|
|
+ frame_listener_attached_ = true;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ EntityManager::getSingleton().SetWorldModule();
|
|
|
+ DialogsManager::getSingleton().SetWorldModule();
|
|
|
+ CreateWorldMap(Root::getSingleton().getSceneManager("Scene"));
|
|
|
+ ScriptManager::getSingleton().RunFile("scripts/worldmap/wm0.lua");
|
|
|
+ CreatePlayer();
|
|
|
+ initialized_ = true;
|
|
|
+ Root::getSingleton().addFrameListener(this);
|
|
|
+ frame_listener_attached_ = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ void WorldmapModule::Suspend(){
|
|
|
+ if (!frame_listener_attached_) return;
|
|
|
+ Root::getSingleton().removeFrameListener(this);
|
|
|
+ frame_listener_attached_ = false;
|
|
|
+ if (world_map_node_ != nullptr) world_map_node_->setVisible(false);
|
|
|
+ player_entity_ = nullptr;
|
|
|
+ highwind_entity_ = nullptr;
|
|
|
+ buggy_entity_ = nullptr;
|
|
|
+ chocobo_entity_ = nullptr;
|
|
|
+ submarine_entity_ = nullptr;
|
|
|
+ tiny_bronco_entity_ = nullptr;
|
|
|
+ current_vehicle_ = VehicleType::ON_FOOT;
|
|
|
+ active_vehicle_terrain_types_.clear();
|
|
|
+ active_function_mesh_index_ = -1;
|
|
|
+ active_function_id_ = -1;
|
|
|
+ cancel_was_pressed_ = false;
|
|
|
}
|
|
|
|
|
|
} // namespace VGears
|