use new gtest macros

started to refactor the persistence layer
network fixes
removed noise dependencies
master
Martin Gerhardy 2016-03-30 08:37:05 +02:00
parent b61b7c58e0
commit c235784e26
92 changed files with 1241 additions and 1565 deletions

View File

@ -23,7 +23,9 @@ include_directories(src/modules)
include_directories(src/modules/ui)
fips_setup(PROJECT engine)
gtest_suite_begin(tests TEMPLATE src/modules/core/tests/main.cpp.in)
fips_add_subdirectory(src)
gtest_suite_end(tests)
fips_finish()
copy_data_files(shared)

View File

@ -12,17 +12,32 @@ clean:
$(Q)rm -f .fips-gen.py
$(Q)rm -f .fips-imports.cmake
run: build
$(Q)./fips run $(TARGET)
eclipse:
$(Q)./fips config linux-eclipse-debug
server: build
$(Q)./fips run server -- -set core_loglevel 4
$(Q)./fips run server
client: build
$(Q)./fips run client -- -set core_loglevel 2
$(Q)./fips run client
debugserver: build
$(Q)./fips gdb server
debugclient: build
$(Q)./fips gdb client
debuggenerate: build
$(Q)./fips gdb worldgenerator -- -set seed 1 -set size 1024
generate: build
$(Q)./fips run worldgenerator -- -set seed 1 -set size 64
$(Q)./fips run worldgenerator -- -set seed 1 -set size 1024
tests: build
$(Q)./fips run tests
tests-list: build
$(Q)./fips run tests -- --gtest_list_tests
tests-filter: build
$(Q)./fips run tests -- --gtest_filter=$(FILTER)

View File

@ -1,18 +1,2 @@
if (DEFINED ENV{SDL2DIR})
set(SDL2_INCLUDE_DIRS ENV{SDL2DIR}/include)
if (FIPS_WINDOWS)
set(SDL2_LIBRARIES ENV{SDL2DIR}/libs/SDL_main.lib ENV{SDL2DIR}/libs/SDL.lib)
endif()
else()
if (FIPS_WINDOWS)
find_package(SDL REQUIRED)
set(SDL2_INCLUDE_DIRS ${SDL_INCLUDE_DIRS})
set(SDL2_LIBRARIES ${SDL_LIBRARIES})
else()
#include(FindPkgConfig)
find_package(PkgConfig)
if (PKG_CONFIG_FOUND)
pkg_search_module(SDL2 REQUIRED sdl2)
endif()
endif()
endif()
include("${PROJECT_SOURCE_DIR}/cmake/macros.cmake")
engine_find(SDL2 SDL.h SDL2 >=2.0.3)

54
cmake/macros.cmake Normal file
View File

@ -0,0 +1,54 @@
#
# macro for the FindLibName.cmake files.
#
# parameters:
# LIB: the library we are trying to find
# HEADER: the header we are trying to find
# SUFFIX: suffix for the include dir
# VERSION: the operator and version that is given to the pkg-config call (e.g. ">=1.0")
#
# Example: engine_find(SDL2_image SDL_image.h SDL2)
#
macro(engine_find LIB HEADER SUFFIX VERSION)
string(TOUPPER ${LIB} PREFIX)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(_PROCESSOR_ARCH "x64")
else()
set(_PROCESSOR_ARCH "x86")
endif()
set(_SEARCH_PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/sw # Fink
/opt/local # DarwinPorts
/opt/csw # Blastwave
/opt
)
find_package(PkgConfig QUIET)
if (PKG_CONFIG_FOUND)
pkg_check_modules(_${PREFIX} QUIET ${LIB}${VERSION})
endif()
find_path(${PREFIX}_INCLUDE_DIRS
NAMES ${HEADER}
HINTS ENV ${PREFIX}DIR
PATH_SUFFIXES include include/${SUFFIX} ${SUFFIX}
PATHS
${_${PREFIX}_INCLUDE_DIRS}
${_SEARCH_PATHS}
)
find_library(${PREFIX}_LIBRARIES
NAMES ${LIB}
HINTS ENV ${PREFIX}DIR
PATH_SUFFIXES lib64 lib lib/${_PROCESSOR_ARCH}
PATHS
${_${PREFIX}_LIBRARY_DIRS}
${_SEARCH_PATHS}
)
unset(_SEARCH_PATHS)
unset(_PROCESSOR_ARCH)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(${PREFIX} REQUIRED_VARS ${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARIES)
mark_as_advanced(${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARIES)
endmacro()

View File

@ -61,7 +61,7 @@ vec3 getSpecularColor(vec3 lightdir, vec3 normal) {
}
vec3 getFogColor() {
return vec3(0.04, 0.29, 0.94);
return vec3(0.0, 0.6, 0.796);
}
float getFog() {

View File

@ -1,8 +1,6 @@
imports:
fips-glm:
git: https://github.com/floooh/fips-glm.git
fips-accidentalnoise:
git: https://github.com/mgerhardy/fips-accidentalnoise.git
fips-enet:
git: https://github.com/mgerhardy/fips-enet.git
fips-polyvox:
@ -26,8 +24,6 @@ imports:
git: https://github.com/mgerhardy/fips-lua.git
fips-zlib:
git: https://github.com/floooh/fips-zlib.git
fips-noisepp:
git: https://github.com/mgerhardy/fips-noisepp.git
exports:
header-dirs:

View File

@ -2,4 +2,3 @@ fips_add_subdirectory(modules)
fips_add_subdirectory(client)
fips_add_subdirectory(server)
fips_add_subdirectory(rcon)
fips_add_subdirectory(worldgenerator)

View File

@ -63,7 +63,7 @@ void Client::onEvent(const network::NewConnectionEvent& event) {
flatbuffers::FlatBufferBuilder fbb;
const std::string& email = core::Var::get("cl_email")->strVal();
const std::string& password = core::Var::get("cl_password")->strVal();
Log::info("Trying to log into the server");
Log::info("Trying to log into the server with %s", email.c_str());
_messageSender->sendClientMessage(_peer, fbb, Type_UserConnect,
CreateUserConnect(fbb, fbb.CreateString(email), fbb.CreateString(password)).Union());
}
@ -202,7 +202,7 @@ core::AppState Client::onInit() {
registerMoveCmd("+move_forward", MOVEFORWARD);
registerMoveCmd("+move_backward", MOVEBACKWARD);
_clearColor = glm::vec3(0.04, 0.29, 0.94);
_clearColor = glm::vec3(0.0, 0.6, 0.796);
_root.SetSkinBg(TBIDC("background"));
new frontend::LoginWindow(this);
@ -424,7 +424,6 @@ void Client::spawn(frontend::ClientEntityId id, const char *name, const glm::vec
_userId = id;
_posLerp.setPosition(_now, pos);
}
;
bool Client::connect(uint16_t port, const std::string& hostname) {
ENetPeer* peer = _network->connect(port, hostname);
@ -436,7 +435,7 @@ bool Client::connect(uint16_t port, const std::string& hostname) {
peer->data = this;
_peer = peer;
Log::error("Connected to server %s:%i", hostname.c_str(), port);
Log::info("Connected to server %s:%i", hostname.c_str(), port);
return true;
}

View File

@ -6,6 +6,5 @@
void SeedHandler::execute(ENetPeer* peer, const void* raw) {
const network::messages::server::Seed* message = static_cast<const network::messages::server::Seed*>(raw);
const long seed = message->seed();
Log::info("Seed is: %li", seed);
_world->load(seed);
_world->setSeed(seed);
}

View File

@ -12,4 +12,4 @@ fips_add_subdirectory(cooldown)
fips_add_subdirectory(backend)
fips_add_subdirectory(frontend)
fips_add_subdirectory(commonlua)
fips_add_subdirectory(dbpost)
fips_add_subdirectory(persistence)

View File

@ -8,10 +8,5 @@ fips_begin_module(attrib)
fips_deps(core commonlua)
fips_end_module()
gtest_begin(attrib)
fips_dir(tests)
fips_files(
AttributesTest.cpp
)
fips_deps(attrib)
gtest_end()
gtest_suite_files(tests tests/AttributesTest.cpp)
gtest_suite_deps(tests attrib)

View File

@ -56,7 +56,7 @@ fips_begin_module(backend)
fips_dir(storage)
fips_files(
UserStore.cpp UserStore.h
PQHandle.cpp PQHandle.h
Persister.cpp Persister.h
StoreCmd.cpp StoreCmd.h
)
@ -75,15 +75,8 @@ fips_begin_module(backend)
)
find_package(SDL2 REQUIRED)
include_directories(${SDL2_INCLUDE_DIRS} .)
fips_deps(network io core util voxel attrib cooldown dbpost simpleai ${SDL2_LIBRARIES})
fips_deps(network io core util voxel attrib cooldown persistence simpleai ${SDL2_LIBRARIES})
fips_end_module()
gtest_begin(backend)
fips_dir(tests)
fips_files(
SpawnMgrTest.cpp
PoiProviderTest.cpp
StorageTest.cpp
)
fips_deps(network io core util voxel attrib cooldown dbpost simpleai backend)
gtest_end()
gtest_suite_files(tests tests/SpawnMgrTest.cpp tests/PoiProviderTest.cpp tests/PersisterTest.cpp)
gtest_suite_deps(tests network io core util voxel attrib cooldown persistence simpleai backend)

View File

@ -1,7 +1,7 @@
#include "EntityStorage.h"
#include "core/Var.h"
#include "User.h"
#include "backend/storage/PQHandle.h"
#include "backend/storage/Persister.h"
#include "backend/storage/UserStore.h"
#define broadcastMsg(msg, type) _messageSender->broadcastServerMessage(fbb, network::messages::server::type, network::messages::server::msg.Union());
@ -30,12 +30,12 @@ void EntityStorage::registerUser(const UserPtr& user) {
EntityId EntityStorage::getUserId(const std::string& user, const std::string& passwd) const {
std::string tmUid = "0";
PQHandle pq;
Persister pq;
pq.init();
int checkId = pq.loadUser(user, passwd, tmUid);
if (checkId == 0) {
const core::VarPtr& autoReg = core::Var::get("user_auto_register", "no");
const core::VarPtr& autoReg = core::Var::get("user_auto_register", "yes");
if (autoReg->strVal() == "yes") {
pq.storeUser(user, passwd, tmUid);
checkId = pq.loadUser(user, passwd, tmUid);

View File

@ -2,6 +2,7 @@
#include "network/MessageSender.h"
#include "Npc.h"
#include "core/Var.h"
namespace backend {

View File

@ -22,16 +22,9 @@ bool ServerLoop::onInit() {
if (!_spawnMgr->init())
return false;
const core::VarPtr& seed = core::Var::get("sv_seed", "1");
const core::VarPtr& size = core::Var::get("sv_size", "128");
class ProgressMonitor: public util::IProgressMonitor {
public:
void done() override {
Log::info("\ndone");
}
} monitor;
if (!_world->load(seed->longVal(), &monitor))
return false;
_world->setSeed(seed->longVal());
if (_aiServer.start()) {
Log::info("Start the ai debug server on 127.0.0.1:11338");
_aiServer.addZone(&_zone);

View File

@ -12,7 +12,7 @@ namespace backend {
class ServerNetworkModule: public NetworkModule {
void configureHandlers() const override {
configureHandler(Type_UserConnect, UserConnectHandler(network::Network &, backend::EntityStorage &));
configureHandler(Type_UserConnect, UserConnectHandler(network::Network &, backend::EntityStorage &, voxel::World &));
configureHandler(Type_UserDisconnect, UserDisconnectHandler());
configureHandler(Type_Attack, AttackHandler());
configureHandler(Type_Move, MoveHandler());

View File

@ -6,8 +6,8 @@
namespace backend {
UserConnectHandler::UserConnectHandler(network::NetworkPtr network, backend::EntityStoragePtr entityStorage) :
_network(network), _entityStorage(entityStorage) {
UserConnectHandler::UserConnectHandler(network::NetworkPtr network, backend::EntityStoragePtr entityStorage, voxel::WorldPtr world) :
_network(network), _entityStorage(entityStorage), _world(world) {
auto data = CreateAuthFailed(_authFailed);
auto msg = CreateServerMessage(_authFailed, Type_AuthFailed, data.Union());
FinishServerMessageBuffer(_authFailed, msg);
@ -31,9 +31,7 @@ void UserConnectHandler::execute(ENetPeer* peer, const void* raw) {
return;
}
// TODO: use the current seed from the world, not from a config var that can be changed during runtime
const long seed = core::Var::get("sv_seed")->longVal();
user->sendSeed(seed);
user->sendSeed(_world->seed());
user->sendUserSpawn();
}

View File

@ -1,6 +1,7 @@
#pragma once
#include "network/Network.h"
#include "voxel/World.h"
#include "backend/entity/EntityStorage.h"
#include <flatbuffers/flatbuffers.h>
@ -11,12 +12,13 @@ class UserConnectHandler: public network::IProtocolHandler {
private:
network::NetworkPtr _network;
backend::EntityStoragePtr _entityStorage;
voxel::WorldPtr _world;
flatbuffers::FlatBufferBuilder _authFailed;
void sendAuthFailed(ENetPeer* peer);
public:
UserConnectHandler(network::NetworkPtr network, backend::EntityStoragePtr entityStorage);
UserConnectHandler(network::NetworkPtr network, backend::EntityStoragePtr entityStorage, voxel::WorldPtr world);
void execute(ENetPeer* peer, const void* message) override;
};

View File

@ -6,8 +6,10 @@
namespace backend {
static const long spawnTime = 15000L;
SpawnMgr::SpawnMgr(voxel::WorldPtr world, EntityStoragePtr entityStorage, network::MessageSenderPtr messageSender, core::TimeProviderPtr timeProvider, AILoaderPtr loader, attrib::ContainerProviderPtr containerProvider, PoiProviderPtr poiProvider) :
_loader(loader), _world(world), _entityStorage(entityStorage), _messageSender(messageSender), _timeProvider(timeProvider), _containerProvider(containerProvider), _poiProvider(poiProvider), _time(0L) {
_loader(loader), _world(world), _entityStorage(entityStorage), _messageSender(messageSender), _timeProvider(timeProvider), _containerProvider(containerProvider), _poiProvider(poiProvider), _time(15000L) {
}
bool SpawnMgr::init() {
@ -82,7 +84,6 @@ int SpawnMgr::spawn(ai::Zone& zone, network::messages::NpcType type, int amount,
}
void SpawnMgr::onFrame(ai::Zone& zone, long dt) {
static const long spawnTime = 15000L;
_time += dt;
if (_time >= spawnTime) {
_time -= spawnTime;

View File

@ -1,61 +0,0 @@
#include "PQHandle.h"
#include "dbpost/StoreInterface.h"
#include "UserStore.h"
#include "core/Log.h"
#include "core/Var.h"
namespace backend {
PQHandle::PQHandle() :
_pqConnection(), _pqStore(&_pqConnection) {
}
PQHandle::~PQHandle() {
_pqConnection.disconnect();
}
void PQHandle::storeUser(const std::string& mail, const std::string& passwd, const std::string& uid) {
UserStore dbUser(mail, passwd, uid);
_pqStore.storeModel(dbUser);
}
int PQHandle::loadUser(const std::string& mail, const std::string& passwd, const std::string& uid) {
const UserStore dbUser(mail, passwd, uid);
_userData = std::move(_pqStore.loadModel(dbUser));
if (_userData.size() > 0) {
const int uid = std::stoi(_userData["userid"]);
return uid;
}
return 0;
}
void PQHandle::close() {
_pqConnection.disconnect();
}
void PQHandle::initTables() {
std::string tmUid = "0";
std::string tmUser = std::string("a");
std::string tmPw = std::string("b");
UserStore dbUser(tmUser, tmPw, tmUid);
_pqStore.createNeeds(dbUser);
}
void PQHandle::init() {
Log::trace("init database connection");
const core::VarPtr& dbName = core::Var::get("db_name", "engine_db");
const core::VarPtr& dbHost = core::Var::get("db_host", "localhost");
const core::VarPtr& dbPw = core::Var::get("db_pw", "ben711cCefIUit887");
const core::VarPtr& dbUser = core::Var::get("db_user", "dbmaster");
_pqConnection.changeDb(dbName->strVal());
_pqConnection.changeHost(dbHost->strVal());
_pqConnection.setLoginData(dbUser->strVal(), dbPw->strVal());
if (_pqConnection.connect() == 0) {
Log::debug("database connection established");
} else {
Log::error("database connection failed");
}
}
}

View File

@ -1,26 +0,0 @@
#pragma once
#include <cstdio>
#include <cstdlib>
#include <cstddef>
#include "dbpost/PQStore.h"
#include "dbpost/PQConnect.h"
namespace backend {
class PQHandle {
private:
dbpost::PQConnect _pqConnection;
dbpost::PQStore _pqStore;
std::unordered_map<std::string, std::string> _userData;
public:
PQHandle();
void init();
void close();
void initTables();
void storeUser(const std::string& uMail, const std::string& uPasswd, const std::string& uid);
int loadUser(const std::string& uMail, const std::string& uPasswd, const std::string& uid);
virtual ~PQHandle();
};
}

View File

@ -0,0 +1,53 @@
#include "Persister.h"
#include "UserStore.h"
#include "core/Log.h"
#include "core/Var.h"
namespace backend {
Persister::Persister() :
_connection(), _store(&_connection) {
}
Persister::~Persister() {
_connection.disconnect();
}
bool Persister::storeUser(const std::string& mail, const std::string& passwd, const std::string& uid) {
UserStore dbUser(mail, passwd, uid);
return _store.storeModel(dbUser);
}
int Persister::loadUser(const std::string& mail, const std::string& passwd, const std::string& uid) {
const UserStore dbUser(mail, passwd, uid);
_userData = std::move(_store.loadModel(dbUser));
if (!_userData.empty()) {
const int uid = core::string::toInt(_userData["userid"]);
return uid;
}
return 0;
}
void Persister::close() {
_connection.disconnect();
}
bool Persister::initTables() {
return true;
}
bool Persister::init() {
Log::trace("init database connection");
const core::VarPtr& dbName = core::Var::get("db_name", "engine_db");
const core::VarPtr& dbHost = core::Var::get("db_host", "localhost");
const core::VarPtr& dbPw = core::Var::get("db_pw", "ben711cCefIUit887");
const core::VarPtr& dbUser = core::Var::get("db_user", "dbmaster");
_connection.changeDb(dbName->strVal());
_connection.changeHost(dbHost->strVal());
_connection.setLoginData(dbUser->strVal(), dbPw->strVal());
return _connection.connect();
}
}

View File

@ -0,0 +1,31 @@
#pragma once
#include <cstdio>
#include <cstdlib>
#include <cstddef>
#include "persistence/Store.h"
#include "persistence/Connection.h"
namespace backend {
class Persister {
private:
persistence::Connection _connection;
persistence::Store _store;
persistence::KeyValueMap _userData;
public:
Persister();
virtual ~Persister();
bool init();
void close();
bool initTables();
bool storeUser(const std::string& uMail, const std::string& uPasswd, const std::string& uid);
int loadUser(const std::string& uMail, const std::string& uPasswd, const std::string& uid);
};
}

View File

@ -8,7 +8,7 @@ void StoreCmd::addComd() {
if (args[0] == "help") {
Log::info("store useradd <name> <password>\tadd a new users");
} else if (args[0] == "init") {
PQHandle pq;
Persister pq;
pq.init();
pq.initTables();
}
@ -17,9 +17,9 @@ void StoreCmd::addComd() {
const std::string tmUid = "0";
const std::string& tmUser = args[1];
const std::string& tmPw = args[2];
PQHandle pq;
pq.init();
pq.storeUser(tmUser, tmPw, tmUid);
Persister persister;
persister.init();
persister.storeUser(tmUser, tmPw, tmUid);
}
}
});

View File

@ -6,7 +6,7 @@
#include "core/Log.h"
#include "core/Var.h"
#include "core/Command.h"
#include "PQHandle.h"
#include "Persister.h"
namespace backend {

View File

@ -5,7 +5,7 @@
namespace backend {
UserStore::UserStore(const std::string& email, const std::string& password, const std::string& userid) :
_email(email), _password(password), _userid(userid) {
persistence::PeristenceModel("user_table"), _email(email), _password(password), _userid(userid) {
}
std::string UserStore::getCreate() const {
@ -23,13 +23,8 @@ void UserStore::update(const std::string& fieldName, const std::string& value) c
}
}
std::string UserStore::getTableName() const {
static const std::string tabName = "user_table";
return tabName;
}
std::unordered_map<std::string, std::string> UserStore::getFields() const {
std::unordered_map<std::string, std::string> storeData;
persistence::Fields UserStore::getFields() const {
persistence::Fields storeData;
storeData["userid"] = _userid;
storeData["user_email"] = _email;
storeData["user_pw_hash"] = _password;

View File

@ -1,30 +1,27 @@
#include <cstdio>
#include <cstdlib>
#include <cstddef>
#include "dbpost/StoreInterface.h"
#include "persistence/PeristenceModel.h"
#pragma once
namespace backend {
class UserStore: public dbpost::StoreInterface {
class UserStore: public persistence::PeristenceModel {
private:
std::string _email;
std::string _password;
std::string _userid;
std::unordered_map<std::string, std::string> _storage;
public:
UserStore(const std::string& email, const std::string& password, const std::string& userid);
std::string getCreate() const override;
std::unordered_map<std::string, std::string> getFields() const override;
persistence::Fields getFields() const override;
bool isSerial(const std::string& fieldname) const override;
virtual void update(const std::string& fieldName, const std::string& value) const override;
std::string getTableName() const override;
void update(const std::string& fieldName, const std::string& value) const override;
};
}

View File

@ -0,0 +1,19 @@
#include "core/tests/AbstractTest.h"
#include "backend/storage/Persister.h"
namespace backend {
class PersisterTest : public core::AbstractTest {
};
TEST(PersisterTest, testSaveAndLoadUser) {
Persister persister;
ASSERT_TRUE(persister.init()) << "Could not establish database connection";
persister.initTables();
// a second call for this will lead to duplicate key constraint errors
persister.storeUser("test@somedomain.com", "12345", "0");
const int userId = persister.loadUser("test@somedomain.com", "12345", "0");
ASSERT_NE(0, userId) << "Could not load user";
}
}

View File

@ -1,14 +0,0 @@
#include <gtest/gtest.h>
#include "backend/storage/PQHandle.h"
namespace backend {
TEST(StorageTest, testCreate) {
#if 0
PQHandle pq;
pq.init();
pq.initTables();
#endif
}
}

View File

@ -5,10 +5,5 @@ fips_begin_module(commonlua)
fips_deps(core lua52)
fips_end_module()
gtest_begin(commonlua)
fips_dir(tests)
fips_files(
LUATest.cpp
)
fips_deps(commonlua)
gtest_end()
gtest_suite_files(tests tests/LUATest.cpp)
gtest_suite_deps(tests commonlua)

View File

@ -7,10 +7,5 @@ fips_begin_module(cooldown)
fips_deps(core)
fips_end_module()
gtest_begin(cooldown)
fips_dir(tests)
fips_files(
CooldownMgrTest.cpp
)
fips_deps(cooldown)
gtest_end()
gtest_suite_files(tests tests/CooldownMgrTest.cpp)
gtest_suite_deps(tests cooldown)

View File

@ -119,6 +119,7 @@ AppState App::onInit() {
core::Var::get(name)->setVal(value);
}
Log::init();
Log::trace("handle %i command line arguments", _argc);
for (int i = 0; i < _argc; ++i) {
// every command is started with a '-'
@ -137,6 +138,8 @@ AppState App::onInit() {
Log::trace("Execute %s with %i arguments", command.c_str(), (int)args.size());
core::Command::execute(command, args);
}
// we might have changed the loglevel from the commandline
Log::init();
return AppState::Running;
}

View File

@ -8,7 +8,7 @@ fips_begin_module(core)
EventBus.h
IFactoryRegistry.h
NonCopyable.h
String.h
String.cpp String.h
Input.h
QuadTree.h
Singleton.h
@ -20,23 +20,13 @@ fips_begin_module(core)
Tokenizer.h
TimeProvider.h TimeProvider.cpp
)
find_package(SDL2 REQUIRED)
include_directories(${SDL2_INCLUDE_DIRS})
fips_deps(io ${SDL2_LIBRARIES})
fips_deps(io)
fips_end_module()
find_package(SDL2 REQUIRED)
message(STATUS "SDL2 libs: ${SDL2_LIBRARIES}")
message(STATUS "SDL2 includes: ${SDL2_INCLUDE_DIRS}")
target_link_libraries(core ${SDL2_LIBRARIES})
target_include_directories(core PUBLIC ${SDL2_INCLUDE_DIRS})
gtest_begin(core)
fips_dir(tests)
fips_files(
RectTest.cpp
ByteStreamTest.cpp
ThreadPoolTest.cpp
EventBusTest.cpp
QuadTreeTest.cpp
VarTest.cpp
CommandTest.cpp
SetTest.cpp
TokenizerTest.cpp
)
fips_deps(core)
gtest_end()
gtest_suite_files(tests tests/AbstractTest.cpp tests/RectTest.cpp tests/ByteStreamTest.cpp tests/ThreadPoolTest.cpp tests/EventBusTest.cpp tests/QuadTreeTest.cpp tests/VarTest.cpp tests/CommandTest.cpp tests/SetTest.cpp tests/TokenizerTest.cpp)
gtest_suite_deps(tests core)

View File

@ -3,65 +3,70 @@
#include <cstring>
#include <SDL.h>
enum {
TRACE = 4, DEBUG = 3, INFO = 2, WARN = 1, ERROR = 0
};
static constexpr int bufSize = 1024;
static SDL_LogPriority _logLevel;
Log::Log() {
_logLevel = core::Var::get("core_loglevel", "2");
}
void Log::vsnprint(const char* msg, va_list args) {
char buf[1024];
SDL_vsnprintf(buf, sizeof(buf), msg, args);
buf[sizeof(buf) - 1] = 0;
if (buf[strlen(buf) - 1] == '\r')
SDL_Log("%s\n", buf);
else
SDL_Log("%s\n", buf);
void Log::init() {
_logLevel = (SDL_LogPriority)core::Var::get("core_loglevel", std::to_string(SDL_LOG_PRIORITY_INFO))->intVal();
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, _logLevel);
}
void Log::trace(const char* msg, ...) {
if (get()._logLevel->intVal() < DEBUG)
if (_logLevel > SDL_LOG_PRIORITY_VERBOSE)
return;
va_list args;
va_start(args, msg);
get().vsnprint(msg, args);
char buf[bufSize];
SDL_vsnprintf(buf, sizeof(buf), msg, args);
buf[sizeof(buf) - 1] = '\0';
SDL_LogVerbose(SDL_LOG_CATEGORY_APPLICATION, "%s\n", buf);
va_end(args);
}
void Log::debug(const char* msg, ...) {
if (get()._logLevel->intVal() < DEBUG)
if (_logLevel > SDL_LOG_PRIORITY_DEBUG)
return;
va_list args;
va_start(args, msg);
get().vsnprint(msg, args);
char buf[bufSize];
SDL_vsnprintf(buf, sizeof(buf), msg, args);
buf[sizeof(buf) - 1] = '\0';
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "%s\n", buf);
va_end(args);
}
void Log::info(const char* msg, ...) {
if (get()._logLevel->intVal() < INFO)
if (_logLevel > SDL_LOG_PRIORITY_INFO)
return;
va_list args;
va_start(args, msg);
get().vsnprint(msg, args);
char buf[bufSize];
SDL_vsnprintf(buf, sizeof(buf), msg, args);
buf[sizeof(buf) - 1] = '\0';
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%s\n", buf);
va_end(args);
}
void Log::warn(const char* msg, ...) {
if (get()._logLevel->intVal() < INFO)
if (_logLevel > SDL_LOG_PRIORITY_WARN)
return;
va_list args;
va_start(args, msg);
get().vsnprint(msg, args);
char buf[bufSize];
SDL_vsnprintf(buf, sizeof(buf), msg, args);
buf[sizeof(buf) - 1] = '\0';
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "%s\n", buf);
va_end(args);
}
void Log::error(const char* msg, ...) {
if (get()._logLevel->intVal() < INFO)
if (_logLevel > SDL_LOG_PRIORITY_ERROR)
return;
va_list args;
va_start(args, msg);
get().vsnprint(msg, args);
char buf[bufSize];
SDL_vsnprintf(buf, sizeof(buf), msg, args);
buf[sizeof(buf) - 1] = '\0';
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s\n", buf);
va_end(args);
}

View File

@ -3,25 +3,9 @@
#include <cstdio>
#include <memory>
namespace core {
class Var;
typedef std::shared_ptr<Var> VarPtr;
}
class Log {
private:
core::VarPtr _logLevel;
Log();
static inline Log& get() {
static Log log;
return log;
}
void vsnprint(const char* msg, va_list args);
public:
static void init();
static void trace(const char* msg, ...) __attribute__((format(printf, 1, 2)));
static void debug(const char* msg, ...) __attribute__((format(printf, 1, 2)));
static void info(const char* msg, ...) __attribute__((format(printf, 1, 2)));

View File

@ -0,0 +1,20 @@
#include "String.h"
namespace core {
namespace string {
std::string format(const char *msg, ...) {
va_list ap;
constexpr std::size_t bufSize = 1024;
char text[bufSize];
va_start(ap, msg);
SDL_vsnprintf(text, bufSize, msg, ap);
text[sizeof(text) - 1] = '\0';
va_end(ap);
return std::string(text);
}
}
}

View File

@ -6,6 +6,7 @@
#include <algorithm>
#include <vector>
#include <stdarg.h>
#include <SDL.h>
namespace core {
namespace string {
@ -89,7 +90,6 @@ inline int getUTF8Next (const char** str)
return character;
}
inline size_t getUTF8Length(const std::string& str) {
size_t result = 0;
const char *string = str.c_str();
@ -102,20 +102,10 @@ inline size_t getUTF8Length(const std::string& str) {
return result;
}
inline std::string format(const char *msg, ...) {
va_list ap;
const std::size_t bufSize = 1024;
char text[bufSize];
va_start(ap, msg);
vsnprintf(text, bufSize, msg, ap);
va_end(ap);
return std::string(text);
}
extern std::string format(const char *msg, ...) SDL_PRINTF_VARARG_FUNC(1);
inline int toInt(const std::string& str) {
return ::atoi(str.c_str());
return SDL_atoi(str.c_str());
}
inline int toLong(const std::string& str) {
@ -127,7 +117,7 @@ inline bool toBool(const std::string& str) {
}
inline float toFloat(const std::string& str) {
return atof(str.c_str());
return SDL_atof(str.c_str());
}
inline void splitString(const std::string& string, std::vector<std::string>& tokens, const std::string& delimiters = " \t\r\n\f\v") {

View File

@ -0,0 +1,20 @@
#include "AbstractTest.h"
#include "core/Log.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
namespace core {
void AbstractTest::SetUp() {
Log::init();
core::EventBusPtr eventBus = core::EventBusPtr(new core::EventBus());
io::FilesystemPtr filesystem = io::FilesystemPtr(new io::Filesystem());
_testApp = new TestApp(filesystem,eventBus);
}
void AbstractTest::TearDown() {
delete _testApp;
}
}

View File

@ -4,6 +4,8 @@
#include "core/EventBus.h"
#include "io/Filesystem.h"
#include "stb_image_write.h"
namespace core {
class AbstractTest: public testing::Test {
@ -19,15 +21,9 @@ private:
TestApp *_testApp;
public:
void SetUp() override {
core::EventBusPtr eventBus = core::EventBusPtr(new core::EventBus());
io::FilesystemPtr filesystem = io::FilesystemPtr(new io::Filesystem());
_testApp = new TestApp(filesystem,eventBus);
}
void SetUp() override;
void TearDown() override {
delete _testApp;
}
void TearDown() override;
};
}

View File

@ -0,0 +1,26 @@
#include <gtest/gtest.h>
class LocalEnv: public ::testing::Environment {
public:
virtual ~LocalEnv() {
}
virtual void SetUp() override {
}
virtual void TearDown() override {
}
};
extern "C" int main (int argc, char *argv[]) {
::testing::AddGlobalTestEnvironment(new LocalEnv);
::testing::InitGoogleTest(&argc, argv);
#if __cpp_exceptions
try {
#endif
return RUN_ALL_TESTS();
#if __cpp_exceptions
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
#endif
}

View File

@ -1,64 +0,0 @@
#include "PQConnect.h"
#include "core/Log.h"
namespace dbpost {
PQConnect::PQConnect() :
_pgConnection(nullptr) {
}
PQConnect::~PQConnect() {
disconnect();
}
void PQConnect::setLoginData(const std::string& username, const std::string& password) {
_password = password;
_user = username;
}
void PQConnect::changeDb(const std::string& dbname) {
_dbname = dbname;
}
void PQConnect::changeHost(const std::string& host) {
_host = host;
}
void PQConnect::changePort(const std::string& port) {
_port = port;
}
int PQConnect::connect() {
//const std::string conninfo = "hostaddr=" + _host + " port=" + _port + " dbname=" + _dbname + " user=" + _user + " password=" + _password;
std::string conninfo = "";
if (!_host.empty())
conninfo += " host=" + _host;
if (!_dbname.empty())
conninfo += " dbname=" + _dbname;
if (!_user.empty())
conninfo += " user=" + _user;
if (!_password.empty())
conninfo += " password=" + _password;
if (!_port.empty())
conninfo += " port=" + _port;
Log::info("Connection: %s", conninfo.c_str());
_pgConnection = PQconnectdb(conninfo.c_str());
if (PQstatus(_pgConnection) != CONNECTION_OK) {
Log::error("Connection to database failed: %s", PQerrorMessage(_pgConnection));
disconnect();
return -1;
}
return 0;
}
void PQConnect::disconnect() {
PQfinish(_pgConnection);
_pgConnection = nullptr;
}
}

View File

@ -1,41 +0,0 @@
#pragma once
#include <unordered_map>
#include <postgresql/libpq-fe.h>
#include "core/NonCopyable.h"
#include "PQConnect.h"
#include "StoreInterface.h"
typedef std::unordered_map<std::string, std::string> TStrStrMap;
typedef std::pair<std::string, std::string> TStrStrPair;
namespace dbpost {
class PQStore : public core::NonCopyable {
private:
std::string sqlBuilder(const StoreInterface& model, bool update) const;
std::string sqlLoadBuilder(const StoreInterface& model, bool update) const;
PQConnect* _connection;
bool _usable;
PGresult* _res;
std::string _lastErrorMsg;
ExecStatusType _lastState;
int _affectedRows;
public:
PQStore(PQConnect* conn);
bool query(const std::string& query);
void trBegin();
void trEnd();
bool checkLastResult();
void storeModel(StoreInterface& model);
void createNeeds(const StoreInterface& model);
std::unordered_map<std::string, std::string> loadModel(const StoreInterface& model);
~PQStore();
PGresult* result() const;
};
inline PGresult* PQStore::result() const {
return _res;
}
}

View File

@ -1 +0,0 @@
#include "StoreInterface.h"

View File

@ -1,21 +0,0 @@
#pragma once
#include <cstdio>
#include <cstdlib>
#include <cstddef>
#include <unordered_map>
namespace dbpost {
class StoreInterface {
public:
virtual ~StoreInterface() {
}
virtual std::string getCreate() const = 0;
virtual std::string getTableName() const = 0;
virtual std::unordered_map<std::string, std::string> getFields() const = 0;
virtual void update(const std::string& fieldName,const std::string& value) const = 0;
virtual bool isSerial(const std::string& fieldname) const = 0;
};
}

View File

@ -1,8 +0,0 @@
#include <gtest/gtest.h>
namespace dbpost {
TEST(PQStoreTest, testFoo) {
}
}

View File

@ -25,15 +25,18 @@ public:
bool OnEvent(const tb::TBWidgetEvent &ev) override {
if ((ev.type == tb::EVENT_TYPE_CLICK && ev.target->GetID() == TBIDC("login")) || ev.special_key == tb::TB_KEY_ENTER) {
TBWidget *email = ev.target->GetParentWindow()->GetWidgetByID(tb::TBID("email"));
TBWidget *password = ev.target->GetParentWindow()->GetWidgetByID(tb::TBID("password"));
TBWindow *window = ev.target->GetParentWindow();
TBWidget *email = window->GetWidgetByID(tb::TBID("email"));
TBWidget *password = window->GetWidgetByID(tb::TBID("password"));
core::Var::get("cl_email")->setVal(email->GetText().CStr());
core::Var::get("cl_password")->setVal(password->GetText().CStr());
const core::VarPtr& port = core::Var::get("cl_port", "11337");
const core::VarPtr& host = core::Var::get("cl_host", "127.0.0.1");
Log::info("Trying to connect to server %s:%i", host->strVal().c_str(), port->intVal());
if (!_client->connect(port->intVal(), host->strVal())) {
Log::info("Failed to connect to server %s:%i", host->strVal().c_str(), port->intVal());
tb::TBStr text;
text.SetFormatted("Failed to connect");
tb::TBMessageWindow *win = new tb::TBMessageWindow(this, TBIDC(""));

View File

@ -61,6 +61,10 @@ ENetPeer* Network::connect(uint16_t port, const std::string& hostname, int maxCh
address.port = port;
ENetPeer *peer = enet_host_connect(_client, &address, maxChannels, 0);
if (peer == nullptr) {
return nullptr;
}
enet_host_flush(_client);
enet_peer_timeout(peer, ENET_PEER_TIMEOUT_LIMIT, ENET_PEER_TIMEOUT_MINIMUM, ENET_PEER_TIMEOUT_MAXIMUM);
return peer;
@ -93,6 +97,7 @@ void Network::disconnectPeer(ENetPeer *peer, uint32_t timeout) {
enet_packet_destroy(event.packet);
break;
case ENET_EVENT_TYPE_DISCONNECT:
_eventBus->publish(DisconnectEvent(event.peer));
success = true;
break;
case ENET_EVENT_TYPE_CONNECT:
@ -104,6 +109,7 @@ void Network::disconnectPeer(ENetPeer *peer, uint32_t timeout) {
/* We've arrived here, so the disconnect attempt didn't */
/* succeed yet. Force the connection down. */
enet_peer_reset(peer);
_eventBus->publish(DisconnectEvent(event.peer));
}
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <enet/enet.h>
#include "core/Log.h"
namespace network {
@ -10,6 +11,7 @@ private:
public:
NewConnectionEvent(ENetPeer* peer) :
_peer(peer) {
Log::info("Connect peer %i", peer->connectID);
}
inline ENetPeer* peer() const {
return _peer;
@ -22,6 +24,7 @@ private:
public:
DisconnectEvent(ENetPeer* peer) :
_peer(peer) {
Log::info("Disconnect peer %i", peer->connectID);
}
inline ENetPeer* peer() const {
return _peer;

View File

@ -13,6 +13,11 @@ struct Vec2 {
y:float;
}
struct IVec2 {
x:int;
y:int;
}
enum NpcType: int {
NONE,

View File

@ -11,6 +11,7 @@ namespace network {
namespace messages {
struct Vec3;
struct Vec2;
struct IVec2;
} // namespace messages
} // namespace network

View File

@ -11,6 +11,7 @@ namespace messages {
struct Vec3;
struct Vec2;
struct IVec2;
enum NpcType {
NpcType_NONE = 0,
@ -73,6 +74,20 @@ MANUALLY_ALIGNED_STRUCT(4) Vec2 FLATBUFFERS_FINAL_CLASS {
};
STRUCT_END(Vec2, 8);
MANUALLY_ALIGNED_STRUCT(4) IVec2 FLATBUFFERS_FINAL_CLASS {
private:
int32_t x_;
int32_t y_;
public:
IVec2(int32_t x, int32_t y)
: x_(flatbuffers::EndianScalar(x)), y_(flatbuffers::EndianScalar(y)) { }
int32_t x() const { return flatbuffers::EndianScalar(x_); }
int32_t y() const { return flatbuffers::EndianScalar(y_); }
};
STRUCT_END(IVec2, 8);
} // namespace messages
} // namespace network

View File

@ -1,76 +0,0 @@
#include "AccidentalNoise.h"
namespace noise {
// TODO: erosion: https://code.google.com/p/accidental-noise-library/wiki/ModuleBasics
#if 0
impad=anl.CImplicitBufferImplicitAdapter(turb, anl.SEAMLESS_NONE, anl.SMappingRanges(0,4,0,4,0,4), true, 0)
erode=anl.CImplicitBufferSimpleErode(impad, 0, 0.35)
blur=anl.CImplicitBufferBlur(erode, 3/256)
bump=anl.CImplicitBufferBumpMap(blur, -1.5, 3.5, 1.5, 0.5, false)
#endif
AccidentalNoise::AccidentalNoise() :
#if 0
groundGradient(0.0, 0.0, 0.0, 1.0, 0.0, 0.0),
plainFractal(anl::EFractalTypes::BILLOW, anl::GRADIENT, anl::QUINTIC, 1, 0.25, false),
plainAutoCorrect(&plainFractal, 0.0, 1.0),
plainScaleOffset(&plainAutoCorrect, 0.125, 0.3),
plainScaleDomain(&plainScaleOffset, 1.0, 0.0),
plainTranslateDomain(&groundGradient, 0.0, &plainScaleDomain, 0.0),
highlandFractal(anl::EFractalTypes::FBM, anl::GRADIENT, anl::QUINTIC, 2, 0.5, false),
highlandAutoCorrect(&highlandFractal, 0.0, 1.0),
highlandScaleOffset(&highlandAutoCorrect, 0.15, 0.0),
highlandScaleDomain(&highlandScaleOffset, 1.0, 0.0),
highlandTranslateDomain(&groundGradient, 0.0, &highlandScaleDomain, 0.0),
mountainFractal(anl::EFractalTypes::RIDGEDMULTI, anl::GRADIENT, anl::QUINTIC, 4, 0.8, false),
mountainAutoCorrect(&mountainFractal, 0.0, 1.0),
mountainScaleOffset(&mountainAutoCorrect, 1.5, -0.75),
mountainScaleDomain(&mountainScaleOffset, 1.0, 0.5),
mountainTranslateDomain(&groundGradient, 0.0, &mountainScaleDomain, 0.0),
terrainControl(anl::EFractalTypes::BILLOW, anl::GRADIENT, anl::QUINTIC, 1, 0.05, false),
terrainAutoCorrect(&terrainControl, 0.0, 1.0),
terrainScaleOffset(&terrainAutoCorrect, 10.0, 0.0),
terrainScaleDomain(&terrainScaleOffset, 1.0, 0.0),
terrainCache(&terrainScaleDomain),
highlandMountainSelect(&highlandTranslateDomain, &mountainTranslateDomain, &terrainCache, 9.0, 1.5),
highlandMountainColorSelect(&highland, &mountain, &terrainCache, 9.0, 0.0),
highlandMountainColorCache(&highlandMountainColorSelect),
plainHighlandSelect(&plainTranslateDomain, &highlandMountainSelect, &terrainCache, 4.0, 1.5),
plainHighlandColorSelect(&plain, &highlandMountainColorSelect, &terrainCache, 4.0, 0.0),
plainHighlandCache(&plainHighlandSelect),
groundBase()
#endif
plainFractal(anl::EFractalTypes::FBM, anl::GRADIENT, anl::QUINTIC, 10, 1.0, false)
{
#if 0
air.setConstant(0.0);
plain.setConstant(1.0);
highland.setConstant(2.0);
mountain.setConstant(3.0);
groundBase.setLowSource(&plainHighlandColorSelect);
groundBase.setHighSource(&air);
groundBase.setControlSource(&plainHighlandCache);
groundBase.setThreshold(0.5);
#endif
}
void AccidentalNoise::setSeed(long seed) {
anl::CMWC4096 rnd;
rnd.setSeed(seed);
#if 0
plainFractal.setSeed(rnd.get());
highlandFractal.setSeed(rnd.get());
mountainFractal.setSeed(rnd.get());
terrainControl.setSeed(rnd.get());
#endif
}
}

View File

@ -1,70 +0,0 @@
#pragma once
#include "core/Random.h"
#include "core/Common.h"
#include <anl_noise.h>
#include <anl_rgba.h>
#include <anl_imaging.h>
#include <mapping.h>
namespace noise {
class AccidentalNoise {
private:
#if 0
anl::CImplicitConstant air;
anl::CImplicitConstant plain;
anl::CImplicitConstant highland;
anl::CImplicitConstant mountain;
anl::CImplicitGradient groundGradient;
anl::CImplicitFractal plainFractal;
anl::CImplicitAutoCorrect plainAutoCorrect;
anl::CImplicitScaleOffset plainScaleOffset;
anl::CImplicitScaleDomain plainScaleDomain;
anl::CImplicitTranslateDomain plainTranslateDomain;
anl::CImplicitFractal highlandFractal;
anl::CImplicitAutoCorrect highlandAutoCorrect;
anl::CImplicitScaleOffset highlandScaleOffset;
anl::CImplicitScaleDomain highlandScaleDomain;
anl::CImplicitTranslateDomain highlandTranslateDomain;
anl::CImplicitFractal mountainFractal;
anl::CImplicitAutoCorrect mountainAutoCorrect;
anl::CImplicitScaleOffset mountainScaleOffset;
anl::CImplicitScaleDomain mountainScaleDomain;
anl::CImplicitTranslateDomain mountainTranslateDomain;
anl::CImplicitFractal terrainControl;
anl::CImplicitAutoCorrect terrainAutoCorrect;
anl::CImplicitScaleOffset terrainScaleOffset;
anl::CImplicitScaleDomain terrainScaleDomain;
anl::CImplicitCache terrainCache;
anl::CImplicitSelect highlandMountainSelect;
anl::CImplicitSelect highlandMountainColorSelect;
anl::CImplicitCache highlandMountainColorCache;
anl::CImplicitSelect plainHighlandSelect;
anl::CImplicitSelect plainHighlandColorSelect;
anl::CImplicitCache plainHighlandCache;
anl::CImplicitSelect groundBase;
#endif
anl::CImplicitFractal plainFractal;
public:
AccidentalNoise();
double get(double x, double y, double z, double worldDimension);
void setSeed(long seed);
};
inline double AccidentalNoise::get(double x, double y, double z, double worldDimension) {
const double scale = 1.0 / worldDimension;
return plainFractal.get(x * scale, y * scale, z * scale);
}
}

View File

@ -1,20 +1,9 @@
fips_begin_module(noise)
fips_files(
PerlinNoise.h PerlinNoise.cpp stb_perlin.h
AccidentalNoise.h AccidentalNoise.cpp
NoisePPNoise.h NoisePPNoise.cpp
WorldShapeNoise.h WorldShapeNoise.cpp
SimplexNoise.h SimplexNoise.cpp
)
fips_deps(core noisepp accidentalnoise)
fips_deps(core)
fips_end_module()
gtest_begin(noise)
fips_dir(tests)
fips_files(
AccidentalNoiseTest.cpp
PerlinNoiseTest.cpp
NoisePPNoiseTest.cpp
NoiseTestHelper.h
)
fips_deps(core noise accidentalnoise)
gtest_end()
gtest_suite_files(tests tests/SimplexNoiseTest.cpp)
gtest_suite_deps(tests core noise)

View File

@ -1,91 +0,0 @@
#include "NoisePPNoise.h"
#include <Noise.h>
#include <NoiseUtils.h>
#include <iostream>
namespace noise {
void NoisePPNoise::init() {
noisepp::ConstantModule lowConstant;
lowConstant.setValue(0);
noisepp::ConstantModule highConstant;
highConstant.setValue(1);
noisepp::BillowModule plainNoise;
plainNoise.setOctaveCount(1);
plainNoise.setQuality(noisepp::NOISE_QUALITY_STD);
plainNoise.setFrequency(0.25);
plainNoise.setLacunarity(2.0);
plainNoise.setPersistence(1);
noisepp::PerlinModule highlandNoise;
highlandNoise.setOctaveCount(2);
highlandNoise.setQuality(noisepp::NOISE_QUALITY_STD);
highlandNoise.setFrequency(0.5);
highlandNoise.setLacunarity(2.0);
highlandNoise.setPersistence(1);
noisepp::RidgedMultiModule mountainNoise;
mountainNoise.setOctaveCount(4);
mountainNoise.setQuality(noisepp::NOISE_QUALITY_STD);
mountainNoise.setFrequency(0.8);
mountainNoise.setLacunarity(2.0);
noisepp::PerlinModule controlNoise;
controlNoise.setOctaveCount(1);
controlNoise.setQuality(noisepp::NOISE_QUALITY_STD);
controlNoise.setFrequency(0.05);
controlNoise.setLacunarity(2.0);
controlNoise.setPersistence(1);
noisepp::SelectModule highlandMountainSelection;
highlandMountainSelection.setControlModule(controlNoise);
highlandMountainSelection.setSourceModule(0, highlandNoise);
highlandMountainSelection.setSourceModule(1, mountainNoise);
highlandMountainSelection.setLowerBound(0.2);
noisepp::SelectModule plainHighlandSelection;
plainHighlandSelection.setControlModule(controlNoise);
plainHighlandSelection.setSourceModule(0, plainNoise);
plainHighlandSelection.setSourceModule(1, highlandMountainSelection);
plainHighlandSelection.setLowerBound(0.0);
_buffer = new noisepp::Real[_w * _h];
// create a builder
noisepp::utils::PlaneBuilder2D builder;
// set the source module
builder.setModule(plainHighlandSelection);
// set the buffer
builder.setDestination(_buffer);
// set the buffer size
builder.setSize(_w, _h);
// set the plane bounds - from (0.5|0) to (1.5|1)
builder.setBounds(0.5, 0, 1.5, 1);
// build
builder.build();
// create an image
noisepp::utils::Image img;
img.create(_w, _h);
// create the renderer and add some gradients to create a heat-vision like material
noisepp::utils::GradientRenderer renderer;
// renderer.addGradient (<value>, noisepp::utils::ColourValue(<red>, <green>, <blue>));
renderer.addGradient(-1.0, noisepp::utils::ColourValue(0.0f, 0.0f, 0.2f));
renderer.addGradient(-0.8, noisepp::utils::ColourValue(0.0f, 0.0f, 0.6f));
renderer.addGradient(0.0, noisepp::utils::ColourValue(1.0f, 0.0f, 0.0f));
renderer.addGradient(0.6, noisepp::utils::ColourValue(1.0f, 1.0f, 0.0f));
renderer.addGradient(1.0, noisepp::utils::ColourValue(1.0f, 1.0f, 1.0f));
// render the image
renderer.renderImage(img, _buffer);
// save the image to an BMP
img.saveBMP("output.bmp");
}
double NoisePPNoise::get(double x, double y, double z, double worldDimension) {
int value = x + y * _h;
return _buffer[value];
}
}

View File

@ -1,21 +0,0 @@
#pragma once
#include <Noise.h>
namespace noise {
class NoisePPNoise {
private:
noisepp::Real *_buffer;
int _w = 64;
int _h = 64;
public:
void init();
double get(double x, double y, double z, double worldDimension);
void setSeed(long seed) {
}
};
}

View File

@ -1,16 +0,0 @@
#include "PerlinNoise.h"
#define STB_PERLIN_IMPLEMENTATION
#include "stb_perlin.h"
namespace noise {
double PerlinNoise::get(double x, double y, double z, double worldDimension) {
const double scale = 1.0 / worldDimension;
const float height = stb_perlin_noise3(x * scale, y * scale, z * scale, 0, 0, 0);
if (::fabs(height) >= y * scale)
return 1.0;
return 0.0;
}
}

View File

@ -1,15 +0,0 @@
#pragma once
#include "stb_perlin.h"
namespace noise {
class PerlinNoise {
public:
double get(double x, double y, double z, double worldDimension);
void setSeed(long seed) {
}
};
}

View File

@ -0,0 +1,31 @@
#include "SimplexNoise.h"
#include <glm/gtc/noise.hpp>
namespace noise {
template<class VecType>
static float Noise(const VecType& pos, int octaves, float persistence, float frequency, float amplitude) {
float total = 0.0f;
float maxAmplitude = amplitude;
for (int i = 0; i < octaves; ++i) {
total += glm::simplex(pos * frequency) * amplitude;
frequency *= 2.0f;
maxAmplitude += amplitude;
amplitude *= persistence;
}
return total / maxAmplitude;
}
float Simplex::Noise2D(const glm::vec2& pos, int octaves, float persistence, float frequency, float amplitude) {
return Noise(pos, octaves, persistence, frequency, amplitude);
}
float Simplex::Noise3D(const glm::vec3& pos, int octaves, float persistence, float frequency, float amplitude) {
return Noise(pos, octaves, persistence, frequency, amplitude);
}
float Simplex::Noise4D(const glm::vec4& pos, int octaves, float persistence, float frequency, float amplitude) {
return Noise(pos, octaves, persistence, frequency, amplitude);
}
}

View File

@ -0,0 +1,229 @@
#pragma once
#include <glm/glm.hpp>
#include <glm/gtc/constants.hpp>
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <cstdint>
#include <functional>
namespace noise {
class Simplex {
public:
/**
* @return A value between [-1,1]
* @param[in] octaves the amount of noise calls that contribute to the final result
* @param[in] persistence the persistence defines how much of the amplitude will be applied to the next noise call (only makes
* sense if you have @c octaves > 1). The higher this value is (ranges from 0-1) the more each new octave will add to the result.
* @param[in] frequency the higher the @c frequency the more deviation you get in your noise (wavelength).
* @param[in] amplitude the amplitude defines how high the noise will be.
*/
static float Noise2D(const glm::vec2& pos, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f);
/**
* @return A value between [-1,1]
* @param[in] octaves the amount of noise calls that contribute to the final result
* @param[in] persistence the persistence defines how much of the amplitude will be applied to the next noise call (only makes
* sense if you have @c octaves > 1). The higher this value is (ranges from 0-1) the more each new octave will add to the result.
* @param[in] frequency the higher the @c frequency the more deviation you get in your noise (wavelength).
* @param[in] amplitude the amplitude defines how high the noise will be.
*/
static float Noise3D(const glm::vec3& pos, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f);
/**
* @return A value between [-1,1]
* @param[in] octaves the amount of noise calls that contribute to the final result
* @param[in] persistence the persistence defines how much of the amplitude will be applied to the next noise call (only makes
* sense if you have @c octaves > 1). The higher this value is (ranges from 0-1) the more each new octave will add to the result.
* @param[in] frequency the higher the @c frequency the more deviation you get in your noise (wavelength).
* @param[in] amplitude the amplitude defines how high the noise will be.
*/
static float Noise4D(const glm::vec4& pos, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f);
/**
* @brief Fills the given target buffer with RGB or RGBA values for the noise (depending on the components).
* @param[in] buffer pointer to the target buffer - must be of size @c width * height * 3
* @param[in] width the width of the image. Make sure that the target buffer has enough space to
* store the needed data for the dimensions you specify here.
* @param[in] height the height of the image. Make sure that the target buffer has enough space
* to store the needed data for the dimensions you specify here.
* @param[in] components 4 for RGBA and 3 for RGB
* @param[in] octaves the amount of noise calls that contribute to the final result
* @param[in] persistence the persistence defines how much of the amplitude will be applied to the next noise call (only makes
* sense if you have @c octaves > 1). The higher this value is (ranges from 0-1) the more each new octave will add to the result.
* @param[in] frequency the higher the @c frequency the more deviation you get in your noise (wavelength).
* @param[in] amplitude the amplitude defines how high the noise will be.
*/
static void Noise2DBuffer(uint8_t* buffer, int width, int height, int components, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
Noise2DBuffer(buffer, width, height, components, glm::vec2(0), octaves, persistence, frequency, amplitude);
}
static void Noise2DBuffer(uint8_t* buffer, int width, int height, int components, const glm::vec2& pos, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
Noise2DBuffer(buffer, width, height, components, pos, noise::Simplex::Noise2D, octaves, persistence, frequency, amplitude);
}
static void Noise2DBuffer(uint8_t* buffer, int width, int height, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
Noise2DBuffer(buffer, width, height, 1, glm::vec2(0), octaves, persistence, frequency, amplitude);
}
static void Noise2DBuffer(uint8_t* buffer, int width, int height, const glm::vec2& pos, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
Noise2DBuffer(buffer, width, height, 1, pos, octaves, persistence, frequency, amplitude);
}
static void Noise2DGray(uint8_t* buffer, int width, int height, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
Noise2DBuffer(buffer, width, height, 3, glm::vec2(0), octaves, persistence, frequency, amplitude);
}
static void Noise2DGray(uint8_t* buffer, int width, int height, const glm::vec2& pos, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
Noise2DBuffer(buffer, width, height, 3, pos, noise::Simplex::Noise2D, octaves, persistence, frequency, amplitude);
}
static void Noise2DGrayA(uint8_t* buffer, int width, int height, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
Noise2DBuffer(buffer, width, height, 4, glm::vec2(0), octaves, persistence, frequency, amplitude);
}
static void Noise2DGrayA(uint8_t* buffer, int width, int height, const glm::vec2& pos, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
Noise2DBuffer(buffer, width, height, 4, pos, noise::Simplex::Noise2D, octaves, persistence, frequency, amplitude);
}
static void Noise2DRGB(uint8_t* buffer, int width, int height, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
Noise2DChannel(buffer, width, height, 3, glm::vec2(0), octaves, persistence, frequency, amplitude);
}
static void Noise2DRGB(uint8_t* buffer, int width, int height, const glm::vec2& pos, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
Noise2DChannel(buffer, width, height, 3, pos, octaves, persistence, frequency, amplitude);
}
static void Noise2DRGBA(uint8_t* buffer, int width, int height, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
Noise2DChannel(buffer, width, height, 4, glm::vec2(0), octaves, persistence, frequency, amplitude);
}
static void Noise2DRGBA(uint8_t* buffer, int width, int height, const glm::vec2& pos, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
Noise2DChannel(buffer, width, height, 4, pos, octaves, persistence, frequency, amplitude);
}
static void Noise2DChannel(uint8_t* buffer, int width, int height, int components, const glm::vec2& pos, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
uint8_t buffer_channel[width * height];
for (int channel = 0; channel < components; ++channel) {
Noise2DBuffer(buffer_channel, width, height, 1, pos + glm::vec2(channel), noise::Simplex::Noise2D, octaves, persistence, frequency, amplitude);
int index = 0;
for (int x = 0; x < width; ++x) {
for (int y = 0; y < height; ++y, ++index) {
buffer[index*3+channel] = buffer_channel[index];
}
}
}
}
template<class Func, class ... Args>
static void Noise2DBuffer(uint8_t* buffer, int width, int height, int components, const glm::vec2& pos, Func&& func, Args&&... args) {
for (int x = 0; x < width; ++x) {
for (int y = 0; y < height; ++y) {
float noise = func(glm::vec2(x, y) + pos, std::forward<Args>(args)...);
float noiseHeight = (noise + 1.0f) * 0.5f;
unsigned char color = (unsigned char) (noiseHeight * 255.0f);
int index = y * (width * components) + (x * components);
const int n = components == 4 ? 3 : components;
for (int i = 0; i < n; ++i) {
buffer[index++] = color;
}
if (components == 4) {
buffer[index] = 255;
}
}
}
}
static void SeamlessNoise2DBuffer(uint8_t* buffer, int size, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
SeamlessNoise2DBuffer(buffer, size, 1, glm::vec4(0), noise::Simplex::Noise4D, octaves, persistence, frequency, amplitude);
}
static void SeamlessNoise2DBuffer(uint8_t* buffer, int size, const glm::vec4& pos, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
SeamlessNoise2DBuffer(buffer, size, 1, pos, noise::Simplex::Noise4D, octaves, persistence, frequency, amplitude);
}
static void SeamlessNoise2DBuffer(uint8_t* buffer, int size, int components, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
SeamlessNoise2DBuffer(buffer, size, components, glm::vec4(0), noise::Simplex::Noise4D, octaves, persistence, frequency, amplitude);
}
static void SeamlessNoise2DBuffer(uint8_t* buffer, int size, int components, const glm::vec4& pos, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
SeamlessNoise2DBuffer(buffer, size, components, pos, noise::Simplex::Noise4D, octaves, persistence, frequency, amplitude);
}
static void SeamlessNoise2DGray(uint8_t* buffer, int size, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
SeamlessNoise2DBuffer(buffer, size, 3, glm::vec4(0), noise::Simplex::Noise4D, octaves, persistence, frequency, amplitude);
}
static void SeamlessNoise2DGray(uint8_t* buffer, int size, const glm::vec4& pos, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
SeamlessNoise2DBuffer(buffer, size, 3, pos, noise::Simplex::Noise4D, octaves, persistence, frequency, amplitude);
}
static void SeamlessNoise2DGrayA(uint8_t* buffer, int size, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
SeamlessNoise2DBuffer(buffer, size, 4, glm::vec4(0), noise::Simplex::Noise4D, octaves, persistence, frequency, amplitude);
}
static void SeamlessNoise2DGrayA(uint8_t* buffer, int size, const glm::vec4& pos, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
SeamlessNoise2DBuffer(buffer, size, 4, pos, noise::Simplex::Noise4D, octaves, persistence, frequency, amplitude);
}
static void SeamlessNoise2DRGB(uint8_t* buffer, int size, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
SeamlessNoise2DChannel(buffer, size, 3, glm::vec4(0), octaves, persistence, frequency, amplitude);
}
static void SeamlessNoise2DRGB(uint8_t* buffer, int size, const glm::vec4& pos, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
SeamlessNoise2DChannel(buffer, size, 3, pos, octaves, persistence, frequency, amplitude);
}
static void SeamlessNoise2DRGBA(uint8_t* buffer, int size, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
SeamlessNoise2DChannel(buffer, size, 4, glm::vec4(0), octaves, persistence, frequency, amplitude);
}
static void SeamlessNoise2DRGBA(uint8_t* buffer, int size, const glm::vec4& pos, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
SeamlessNoise2DChannel(buffer, size, 4, pos, octaves, persistence, frequency, amplitude);
}
static void SeamlessNoise2DChannel(uint8_t* buffer, int size, int components, const glm::vec4& pos, int octaves = 1, float persistence = 1.0f, float frequency = 1.0f, float amplitude = 1.0f) {
uint8_t buffer_channel[size * size];
for (int channel = 0; channel < components; ++channel) {
SeamlessNoise2DBuffer(buffer_channel, size, 1, pos + glm::vec4(channel), noise::Simplex::Noise4D, octaves, persistence, frequency, amplitude);
int index = 0;
for (int x = 0; x < size; ++x) {
for (int y = 0; y < size; ++y, ++index) {
buffer[index*3+channel] = buffer_channel[index];
}
}
}
}
template<class Func, class ... Args>
static void SeamlessNoise2DBuffer(uint8_t* buffer, int size, int components, const glm::vec4& pos, Func&& func, Args&&... args) {
// seamless noise: http://www.gamedev.net/blog/33/entry-2138456-seamless-noise/
const float pi2 = 2.0f * glm::pi<float>();
const float d = 1.0f / size;
float s = 0.0f;
for (int x = 0; x < size; x++, s+=d) {
const float s_pi2 = s*pi2;
const float nx = glm::cos(s_pi2);
const float nz = glm::sin(s_pi2);
float t = 0.0f;
for (int y = 0; y < size; y++, t+=d) {
const float t_pi2 = t*pi2;
float ny = glm::cos(t_pi2);
float nw = glm::sin(t_pi2);
float noise = func(glm::vec4(nx,ny,nz,nw) + pos, std::forward<Args>(args)...);
noise = (noise + 0.5f) * 1.0;
noise = std::min(std::max(noise, 0.0f), 1.0f);
unsigned char color = (unsigned char) (noise * 255.0f);
int index = y * (size * components) + (x * components);
const int fill_components = components == 4 ? 3 : components;
for (int i = 0; i < fill_components; ++i) {
buffer[index++] = color;
}
if (components == 4) {
buffer[index] = 255;
}
}
}
}
};
}

View File

@ -1,59 +0,0 @@
#include "WorldShapeNoise.h"
namespace noise {
WorldShapeNoise::WorldShapeNoise() :
_sphere(1.0, 0.5, 0.0, 0.5),
_shape(),
_elevationBase(anl::EFractalTypes::FBM, anl::GRADIENT, anl::QUINTIC, 6, 2, true),
_elevationAutoCorrect(&_elevationBase, 0.0, 1.0),
_elevationTurbulence(anl::EFractalTypes::RIDGEDMULTI, anl::GRADIENT, anl::QUINTIC, 8, 1, true),
_elevationTurbulenceAutoCorrect(&_elevationTurbulence, 0.0, 1.0),
_elevationTurbulenceTranslateDomain(&_elevationAutoCorrect, 0.0, &_elevationTurbulenceAutoCorrect, 0.0),
_elevation(anl::MULTIPLY, &_elevationTurbulenceTranslateDomain, &_shape),
_groundBase()
{
_shape.setOperation(anl::EASEQUINTIC);
_shape.setSource(&_sphere);
_air.setConstant(0.0);
_soil.setConstant(1.0);
_groundBase.setLowSource(&_soil);
_groundBase.setHighSource(&_air);
_groundBase.setControlSource(&_elevation);
_groundBase.setThreshold(0.5);
}
void WorldShapeNoise::generateImage() {
anl::CRGBACompositeChannels composite;
composite.setMode(anl::RGB);
composite.setRedSource(&_shape);
composite.setGreenSource(&_shape);
composite.setBlueSource(&_shape);
composite.setAlphaSource(1.0);
anl::TArray2D<TVec4D<float>> img(256, 256);
anl::SMappingRanges ranges;
ranges.mapx0 = -1;
ranges.mapy0 = -1;
ranges.mapx1 = 1;
ranges.mapy1 = 1;
mapRGBA2D(anl::SEAMLESS_NONE, img ,composite ,ranges, 0);
//Just for debugging purpose
saveRGBAArray((char*)"heightmap.tga", &img);
Log::info("+++++++++++++++generateImage");
}
void WorldShapeNoise::setSeed(long seed) {
anl::CMWC4096 rnd;
rnd.setSeed(seed);
}
}

View File

@ -1,46 +0,0 @@
#pragma once
#include "core/Random.h"
#include "core/Common.h"
#include <anl_noise.h>
#include <anl_rgba.h>
#include <anl_imaging.h>
#include <mapping.h>
namespace noise {
class WorldShapeNoise {
private:
anl::CImplicitSphere _sphere;
anl::CImplicitMath _shape;
anl::CImplicitFractal _elevationBase;
anl::CImplicitAutoCorrect _elevationAutoCorrect;
anl::CImplicitFractal _elevationTurbulence;
anl::CImplicitAutoCorrect _elevationTurbulenceAutoCorrect;
anl::CImplicitTranslateDomain _elevationTurbulenceTranslateDomain;
anl::CImplicitMath _elevation;
anl::CImplicitConstant _air;
anl::CImplicitConstant _soil;
anl::CImplicitSelect _groundBase;
public:
WorldShapeNoise();
double get(double x, double y, double z, double worldDimension = 128.0);
void generateImage();
void setSeed(long seed);
};
inline double WorldShapeNoise::get(double x, double y, double z, double worldDimension) {
const double scale = 1.0 / worldDimension;
return _groundBase.get(x * scale, y * scale, z * scale);
}
}

View File

@ -1,175 +0,0 @@
// stb_perlin.h - v0.2 - perlin noise
// public domain single-file C implementation by Sean Barrett
//
// to create the implementation,
// #define STB_PERLIN_IMPLEMENTATION
// in *one* C/CPP file that includes this file.
// Documentation:
//
// float stb_perlin_noise3( float x,
// float y,
// float z,
// int x_wrap=0,
// int y_wrap=0,
// int z_wrap=0)
//
// This function computes a random value at the coordinate (x,y,z).
// Adjacent random values are continuous but the noise fluctuates
// its randomness with period 1, i.e. takes on wholly unrelated values
// at integer points. Specifically, this implements Ken Perlin's
// revised noise function from 2002.
//
// The "wrap" parameters can be used to create wraparound noise that
// wraps at powers of two. The numbers MUST be powers of two. Specify
// 0 to mean "don't care". (The noise always wraps every 256 due
// details of the implementation, even if you ask for larger or no
// wrapping.)
#ifdef __cplusplus
extern "C" float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap);
#else
extern float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap);
#endif
#ifdef STB_PERLIN_IMPLEMENTATION
#include <math.h> // floor()
// not same permutation table as Perlin's reference to avoid copyright issues;
// Perlin's table can be found at http://mrl.nyu.edu/~perlin/noise/
// @OPTIMIZE: should this be unsigned char instead of int for cache?
static int stb__perlin_randtab[512] =
{
23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123,
152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72,
175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240,
8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57,
225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233,
94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172,
165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243,
65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122,
26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76,
250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246,
132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3,
91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231,
38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221,
131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62,
27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135,
61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5,
// and a second copy so we don't need an extra mask or static initializer
23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123,
152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72,
175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240,
8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57,
225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233,
94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172,
165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243,
65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122,
26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76,
250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246,
132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3,
91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231,
38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221,
131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62,
27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135,
61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5,
};
static float stb__perlin_lerp(float a, float b, float t)
{
return a + (b-a) * t;
}
// different grad function from Perlin's, but easy to modify to match reference
static float stb__perlin_grad(int hash, float x, float y, float z)
{
static float basis[12][4] =
{
{ 1, 1, 0 },
{ -1, 1, 0 },
{ 1,-1, 0 },
{ -1,-1, 0 },
{ 1, 0, 1 },
{ -1, 0, 1 },
{ 1, 0,-1 },
{ -1, 0,-1 },
{ 0, 1, 1 },
{ 0,-1, 1 },
{ 0, 1,-1 },
{ 0,-1,-1 },
};
// perlin's gradient has 12 cases so some get used 1/16th of the time
// and some 2/16ths. We reduce bias by changing those fractions
// to 5/16ths and 6/16ths, and the same 4 cases get the extra weight.
static unsigned char indices[64] =
{
0,1,2,3,4,5,6,7,8,9,10,11,
0,9,1,11,
0,1,2,3,4,5,6,7,8,9,10,11,
0,1,2,3,4,5,6,7,8,9,10,11,
0,1,2,3,4,5,6,7,8,9,10,11,
0,1,2,3,4,5,6,7,8,9,10,11,
};
// if you use reference permutation table, change 63 below to 15 to match reference
float *grad = basis[indices[hash & 63]];
return grad[0]*x + grad[1]*y + grad[2]*z;
}
float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap)
{
float u,v,w;
float n000,n001,n010,n011,n100,n101,n110,n111;
float n00,n01,n10,n11;
float n0,n1;
unsigned int x_mask = (x_wrap-1) & 255;
unsigned int y_mask = (y_wrap-1) & 255;
unsigned int z_mask = (z_wrap-1) & 255;
int px = (int) floor(x);
int py = (int) floor(y);
int pz = (int) floor(z);
int x0 = px & x_mask, x1 = (px+1) & x_mask;
int y0 = py & y_mask, y1 = (py+1) & y_mask;
int z0 = pz & z_mask, z1 = (pz+1) & z_mask;
int r0,r1, r00,r01,r10,r11;
#define stb__perlin_ease(a) (((a*6-15)*a + 10) * a * a * a)
x -= px; u = stb__perlin_ease(x);
y -= py; v = stb__perlin_ease(y);
z -= pz; w = stb__perlin_ease(z);
r0 = stb__perlin_randtab[x0];
r1 = stb__perlin_randtab[x1];
r00 = stb__perlin_randtab[r0+y0];
r01 = stb__perlin_randtab[r0+y1];
r10 = stb__perlin_randtab[r1+y0];
r11 = stb__perlin_randtab[r1+y1];
n000 = stb__perlin_grad(stb__perlin_randtab[r00+z0], x , y , z );
n001 = stb__perlin_grad(stb__perlin_randtab[r00+z1], x , y , z-1 );
n010 = stb__perlin_grad(stb__perlin_randtab[r01+z0], x , y-1, z );
n011 = stb__perlin_grad(stb__perlin_randtab[r01+z1], x , y-1, z-1 );
n100 = stb__perlin_grad(stb__perlin_randtab[r10+z0], x-1, y , z );
n101 = stb__perlin_grad(stb__perlin_randtab[r10+z1], x-1, y , z-1 );
n110 = stb__perlin_grad(stb__perlin_randtab[r11+z0], x-1, y-1, z );
n111 = stb__perlin_grad(stb__perlin_randtab[r11+z1], x-1, y-1, z-1 );
n00 = stb__perlin_lerp(n000,n001,w);
n01 = stb__perlin_lerp(n010,n011,w);
n10 = stb__perlin_lerp(n100,n101,w);
n11 = stb__perlin_lerp(n110,n111,w);
n0 = stb__perlin_lerp(n00,n01,v);
n1 = stb__perlin_lerp(n10,n11,v);
return stb__perlin_lerp(n0,n1,u);
}
#endif // STB_PERLIN_IMPLEMENTATION

View File

@ -1,21 +0,0 @@
#include <gtest/gtest.h>
#include "noise/AccidentalNoise.h"
#include "NoiseTestHelper.h"
namespace noise {
TEST(AccidentalNoiseTest, testNoise) {
AccidentalNoise noise;
noise.setSeed(1L);
const int worldHeight = 256;
const int worldSize = 32;
#if 0
anl::CArray3Dd a(worldSize, worldHeight, worldSize);
anl::CImplicitModuleBase* m = noise.get();
anl::SMappingRanges ranges;
anl::map3D(anl::SEAMLESS_NONE, a, *m, ranges);
#endif
printNoise(noise, worldSize, worldHeight, worldSize);
}
}

View File

@ -1,16 +0,0 @@
#include <gtest/gtest.h>
#include "noise/NoisePPNoise.h"
#include "NoiseTestHelper.h"
namespace noise {
TEST(NoisePPNoiseTest, testNoise) {
NoisePPNoise noise;
noise.setSeed(1L);
const int worldHeight = 32;
const int worldSize = 32;
noise.init();
printNoise(noise, worldSize, worldHeight, worldSize);
}
}

View File

@ -1,25 +0,0 @@
#pragma once
#include "core/Log.h"
#include <sstream>
namespace noise {
template<class NOISE>
inline void printNoise(NOISE& noise, float width, float height, float depth) {
for (float y = 0; y < 32; ++y) {
Log::info("y: %f", y);
Log::info("------------");
for (float z = depth-1; z >= 0; --z) {
Log::info("%.3f: ", z);
std::stringstream sb;
for (float x = 0; x < width; ++x) {
sb << noise.get(x, y, z, 256.0) << " ";
}
Log::info("%s", sb.str().c_str());
}
Log::info("------------");
}
}
}

View File

@ -1,15 +0,0 @@
#include <gtest/gtest.h>
#include "noise/PerlinNoise.h"
#include "NoiseTestHelper.h"
namespace noise {
TEST(PerlinNoiseTest, testNoise) {
PerlinNoise noise;
noise.setSeed(1L);
const int worldHeight = 16;
const int worldSize = 16;
printNoise(noise, worldSize, worldHeight, worldSize);
}
}

View File

@ -0,0 +1,95 @@
#include "core/tests/AbstractTest.h"
#include "noise/SimplexNoise.h"
#include <glm/gtc/noise.hpp>
#include <glm/gtc/constants.hpp>
namespace noise {
class SimplexNoiseTest: public core::AbstractTest {
protected:
const int components = 4;
const int w = 256;
const int h = 256;
bool WriteImage(const char* name, uint8_t* buffer) {
return stbi_write_png(name, w, h, components, buffer, w * components) != 0;
}
};
TEST_F(SimplexNoiseTest, testLandscapeMountains) {
uint8_t buffer[w * h * components];
for (int x = 0; x < w; ++x) {
for (int y = 0; y < h; ++y) {
const glm::vec2 pos(x, y);
float landscapeNoise = noise::Simplex::Noise2D(pos, 4, 0.3f, 0.01f);
ASSERT_LE(landscapeNoise, 1.0f)<< "Noise is bigger than 1.0: " << landscapeNoise;
ASSERT_GE(landscapeNoise, -1.0f)<< "Noise is less than -1.0: " << landscapeNoise;
float noiseNormalized = (landscapeNoise + 1.0f) * 0.5f;
ASSERT_LE(noiseNormalized, 1.0f)<< "Noise is bigger than 1.0: " << noiseNormalized;
ASSERT_GE(noiseNormalized, 0.0f)<< "Noise is less than 0.0: " << noiseNormalized;
float mountainNoise = noise::Simplex::Noise2D(pos, 4, 0.3f, 0.0075f);
float mountainNoiseNormalized = (mountainNoise + 1.0f) * 0.5f;
float mountainMultiplier = mountainNoiseNormalized * 2.3f;
float noiseHeight = glm::clamp(noiseNormalized * mountainMultiplier, 0.0f, 1.0f);
unsigned char color = (unsigned char) (noiseHeight * 255.0f);
ASSERT_LE(color, 255)<< "Color is bigger than 255: " << color;
ASSERT_GE(color, 0)<< "Color is less than 0: " << color;
int index = y * (w * components) + (x * components);
const int n = components == 4 ? 3 : components;
for (int i = 0; i < n; ++i) {
buffer[index++] = color;
}
if (components == 4)
buffer[index] = 255;
}
}
ASSERT_TRUE(WriteImage("testNoiseLandscapeMountains.png", buffer));
}
TEST_F(SimplexNoiseTest, test2DNoise) {
uint8_t buffer[w * h * components];
for (int x = 0; x < w; ++x) {
for (int y = 0; y < h; ++y) {
const glm::vec2 pos(x, y);
const float noise = Simplex::Noise2D(pos, 2, 1.0f, 0.5f, 1.5f);
ASSERT_LE(noise, 1.0f)<< "Noise is bigger than 1.0: " << noise;
ASSERT_GE(noise, -1.0f)<< "Noise is less than -1.0: " << noise;
float normalized = glm::clamp(noise * 0.5f + 0.5f, 0.0f, 1.0f);
ASSERT_LE(normalized, 1.0f)<< "Noise is bigger than 1.0: " << normalized;
ASSERT_GE(normalized, 0.0f)<< "Noise is less than 0.0: " << normalized;
unsigned char color = (unsigned char) (normalized * 255.0f);
ASSERT_LE(color, 255)<< "Color is bigger than 255: " << color;
ASSERT_GE(color, 0)<< "Color is less than 0: " << color;
int index = y * (w * components) + (x * components);
const int n = components == 4 ? 3 : components;
for (int i = 0; i < n; ++i) {
buffer[index++] = color;
}
if (components == 4)
buffer[index] = 255;
}
}
ASSERT_TRUE(WriteImage("testNoise2d.png", buffer));
}
TEST_F(SimplexNoiseTest, test2DNoiseGray) {
const int width = 100;
const int height = 100;
const int components = 3;
uint8_t buffer[width * height * components];
Simplex::Noise2DGray(buffer, width, height, 1, 1.0, 1.0, 1.0);
ASSERT_TRUE(stbi_write_png("testNoiseGray.png", width, height, components, buffer, width * components) != 0);
}
TEST_F(SimplexNoiseTest, test2DNoiseColorMap) {
const int width = 256;
const int height = 256;
const int components = 3;
uint8_t buffer[width * height * components];
noise::Simplex::SeamlessNoise2DRGB(buffer, width, 3, 0.3f, 0.7f);
ASSERT_TRUE(stbi_write_png("testNoiseColorMap.png", width, height, components, buffer, width * components) != 0);
}
}

View File

@ -1,8 +1,8 @@
fips_begin_module(dbpost)
fips_begin_module(persistence)
fips_files(
PQConnect.cpp PQConnect.h
PQStore.cpp PQStore.h
StoreInterface.cpp StoreInterface.h
Connection.cpp Connection.h
Store.cpp Store.h
PeristenceModel.cpp PeristenceModel.h
)
set(PostgreSQL_ADDITIONAL_VERSIONS "9.3" "9.4.1")
set(PostgreSQL_ADDITIONAL_SEARCH_PATHS "/usr/include/postgresql/9.3/server" "/usr/include/postgresql/9.4/server")
@ -11,10 +11,5 @@ fips_begin_module(dbpost)
fips_deps(core ${PostgreSQL_LIBRARIES})
fips_end_module()
gtest_begin(dbpost)
fips_dir(tests)
fips_files(
PQStoreTest.cpp
)
fips_deps(dbpost)
gtest_end()
gtest_suite_files(tests tests/StoreTest.cpp)
gtest_suite_deps(tests persistence)

View File

@ -0,0 +1,66 @@
#include "Connection.h"
#include "core/Log.h"
namespace persistence {
Connection::Connection() :
_pgConnection(nullptr), _port(0) {
}
Connection::~Connection() {
disconnect();
}
void Connection::setLoginData(const std::string& username, const std::string& password) {
_password = password;
_user = username;
}
void Connection::changeDb(const std::string& dbname) {
_dbname = dbname;
}
void Connection::changeHost(const std::string& host) {
_host = host;
}
void Connection::changePort(uint16_t port) {
_port = port;
}
inline std::string Connection::escape(const std::string& value) const {
// TODO: escape ' inside the value
return "'" + value + "'";
}
bool Connection::connect() {
std::string conninfo = "connect_timeout='2'";
if (!_host.empty())
conninfo += " host=" + escape(_host);
if (!_dbname.empty())
conninfo += " dbname=" + escape(_dbname);
if (!_user.empty())
conninfo += " user=" + escape(_user);
if (!_password.empty())
conninfo += " password=" + escape(_password);
if (_port > 0)
conninfo += " port=" + escape(std::to_string(_port));
_pgConnection = PQconnectdb(conninfo.c_str());
if (PQstatus(_pgConnection) != CONNECTION_OK) {
Log::error("Connection to database failed: %s", PQerrorMessage(_pgConnection));
disconnect();
return false;
}
return true;
}
void Connection::disconnect() {
PQfinish(_pgConnection);
_pgConnection = nullptr;
}
}

View File

@ -3,29 +3,39 @@
#include <string>
#include <postgresql/libpq-fe.h>
namespace dbpost {
namespace persistence {
class PQConnect {
class Connection {
private:
PGconn* _pgConnection;
std::string _host;
std::string _port;
std::string _dbname;
std::string _user;
std::string _password;
uint16_t _port;
std::string escape(const std::string& value) const;
public:
PQConnect();
~PQConnect();
Connection();
~Connection();
void setLoginData(const std::string& username, const std::string& password);
void changeHost(const std::string& host);
void changePort(const std::string& port);
void changePort(uint16_t port);
void changeDb(const std::string& dbname);
void disconnect();
int connect();
bool connect();
PGconn* connection() const;
};
inline PGconn* PQConnect::connection() const {
inline PGconn* Connection::connection() const {
return _pgConnection;
}

View File

@ -0,0 +1,9 @@
#include "PeristenceModel.h"
namespace persistence {
PeristenceModel::PeristenceModel(const std::string& tableName) :
_tableName(tableName) {
}
}

View File

@ -0,0 +1,34 @@
#pragma once
#include <cstdio>
#include <cstdlib>
#include <cstddef>
#include <unordered_map>
namespace persistence {
typedef std::unordered_map<std::string, std::string> Fields;
class PeristenceModel {
protected:
const std::string _tableName;
public:
PeristenceModel(const std::string& tableName);
virtual ~PeristenceModel() {
}
virtual std::string getCreate() const = 0;
virtual const std::string& getTableName() const {
return _tableName;
}
virtual Fields getFields() const = 0;
virtual void update(const std::string& fieldName,const std::string& value) const = 0;
virtual bool isSerial(const std::string& fieldname) const = 0;
};
}

View File

@ -1,31 +1,27 @@
#include "PQStore.h"
#include "StoreInterface.h"
#include "Store.h"
#include "core/Log.h"
#include <sstream>
namespace dbpost {
namespace persistence {
PQStore::PQStore(PQConnect* conn) :
Store::Store(Connection* conn) :
_connection(conn), _usable(true), _res(nullptr), _lastState(PGRES_EMPTY_QUERY), _affectedRows(0) {
}
void PQStore::storeModel(StoreInterface& model) {
bool Store::storeModel(PeristenceModel& model) {
const std::string& insertSql = sqlBuilder(model, false);
//trBegin();
query(insertSql);
//trEnd();
return query(insertSql);
}
void PQStore::createNeeds(const StoreInterface& model) {
bool Store::createNeeds(const PeristenceModel& model) {
const std::string& crSql = model.getCreate();
query(crSql);
return query(crSql);
}
std::unordered_map<std::string, std::string> PQStore::loadModel(const StoreInterface& model) {
KeyValueMap Store::loadModel(const PeristenceModel& model) {
const std::string& loadSql = sqlLoadBuilder(model, false);
Log::trace("sql used %s", loadSql.c_str());
std::unordered_map<std::string, std::string> dbResult;
KeyValueMap dbResult;
if (query(loadSql) && _affectedRows == 1) {
const int nFields = PQnfields(_res);
for (int i = 0; i < nFields; ++i) {
@ -40,13 +36,13 @@ std::unordered_map<std::string, std::string> PQStore::loadModel(const StoreInter
return dbResult;
}
std::string PQStore::sqlBuilder(const StoreInterface& model, bool update) const {
std::string Store::sqlBuilder(const PeristenceModel& model, bool update) const {
std::stringstream insertSql;
insertSql << "INSERT INTO " << model.getTableName() << " ";
std::stringstream fieldKeys;
std::stringstream valueKeys;
const std::unordered_map<std::string, std::string>& fields = model.getFields();
const Fields& fields = model.getFields();
std::string add = "";
for (auto p = fields.begin(); p != fields.end(); ++p) {
@ -65,11 +61,11 @@ std::string PQStore::sqlBuilder(const StoreInterface& model, bool update) const
return str;
}
std::string PQStore::sqlLoadBuilder(const StoreInterface& model, bool update) const {
std::string Store::sqlLoadBuilder(const PeristenceModel& model, bool update) const {
std::string loadSql = "SELECT * FROM " + model.getTableName() + " ";
std::string fieldKeys = "";
const std::unordered_map<std::string, std::string>& fields = model.getFields();
const Fields& fields = model.getFields();
std::string add = "";
for (auto p = fields.begin(); p != fields.end(); ++p) {
@ -85,7 +81,7 @@ std::string PQStore::sqlLoadBuilder(const StoreInterface& model, bool update) co
return loadSql;
}
void PQStore::trBegin() {
void Store::trBegin() {
if (!_usable)
return;
@ -96,7 +92,7 @@ void PQStore::trBegin() {
}
}
void PQStore::trEnd() {
void Store::trEnd() {
if (!_usable)
return;
@ -107,17 +103,16 @@ void PQStore::trEnd() {
}
}
bool PQStore::checkLastResult() {
bool Store::checkLastResult() {
_affectedRows = 0;
Log::info("get result");
if (_res != nullptr)
_lastState = PQresultStatus(_res);
else
if (_res == nullptr)
return false;
_lastState = PQresultStatus(_res);
if ((_lastState == PGRES_EMPTY_QUERY) || (_lastState == PGRES_BAD_RESPONSE) || (_lastState == PGRES_FATAL_ERROR)) {
PQclear(_res);
char* msg = PQerrorMessage(_connection->connection());
const char* msg = PQerrorMessage(_connection->connection());
_lastErrorMsg = std::string(msg);
Log::error("Failed to execute sql: %s ", _lastErrorMsg.c_str());
@ -133,7 +128,7 @@ bool PQStore::checkLastResult() {
if (_lastState == PGRES_TUPLES_OK) {
_affectedRows = PQntuples(_res);
Log::info("Data read %i", _affectedRows);
Log::trace("Affected rows on read %i", _affectedRows);
return true;
}
@ -142,7 +137,7 @@ bool PQStore::checkLastResult() {
return false;
}
bool PQStore::query(const std::string& query) {
bool Store::query(const std::string& query) {
if (_usable) {
Log::trace("SEND: %s", query.c_str());
_res = PQexec(_connection->connection(), query.c_str());
@ -152,7 +147,7 @@ bool PQStore::query(const std::string& query) {
return false;
}
PQStore::~PQStore() {
Store::~Store() {
// TODO: assigning a nullptr is not possible for a reference
//_connection = nullptr;
}

View File

@ -0,0 +1,51 @@
#pragma once
#include "Connection.h"
#include "PeristenceModel.h"
#include <unordered_map>
#include <postgresql/libpq-fe.h>
#include "core/NonCopyable.h"
namespace persistence {
typedef std::unordered_map<std::string, std::string> KeyValueMap;
typedef std::pair<std::string, std::string> KeyValuePair;
class Store : public core::NonCopyable {
private:
Connection* _connection;
bool _usable;
PGresult* _res;
std::string _lastErrorMsg;
ExecStatusType _lastState;
int _affectedRows;
std::string sqlBuilder(const PeristenceModel& model, bool update) const;
std::string sqlLoadBuilder(const PeristenceModel& model, bool update) const;
public:
Store(Connection* conn);
~Store();
bool query(const std::string& query);
void trBegin();
void trEnd();
bool checkLastResult();
bool storeModel(PeristenceModel& model);
bool createNeeds(const PeristenceModel& model);
KeyValueMap loadModel(const PeristenceModel& model);
PGresult* result() const;
};
inline PGresult* Store::result() const {
return _res;
}
}

View File

@ -0,0 +1,12 @@
#include "core/tests/AbstractTest.h"
#include "persistence/Store.h"
namespace persistence {
class StoreTest : public core::AbstractTest {
};
TEST(StoreTest, testFoo) {
}
}

View File

@ -12,13 +12,5 @@ fips_begin_module(ui)
fips_deps(core io video turbobadger ${SDL2_LIBRARIES} ${OPENGL_LIBRARIES})
fips_end_module()
gtest_begin(ui)
fips_dir(tests)
fips_files(
KeybindingParserTest.cpp
)
find_package(OpenGL)
find_package(SDL2 REQUIRED)
fips_include_directories(. ${SDL2_INCLUDE_DIRS} ${OPENGL_INCLUDE_DIR})
fips_deps(core io video turbobadger ui ${SDL2_LIBRARIES} ${OPENGL_LIBRARIES})
gtest_end()
gtest_suite_files(tests tests/KeybindingParserTest.cpp)
gtest_suite_deps(tests ui)

View File

@ -294,7 +294,7 @@ core::AppState UIApp::onInit() {
return core::AppState::Cleanup;
}
font->RenderGlyphs(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~•·åäöÅÄÖ");
font->RenderGlyphs(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNORSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~•·åäöÅÄÖ");
_root.SetRect(tb::TBRect(0, 0, _width, _height));
_root.SetSkinBg(TBIDC("background"));

View File

@ -4,21 +4,21 @@ namespace util {
class IProgressMonitor {
protected:
int _max;
int _steps;
long _max;
long _steps;
public:
IProgressMonitor(int max = 100) :
_max(max), _steps(0) {
IProgressMonitor(long max = 100l):
_max(max), _steps(0l) {
}
virtual ~IProgressMonitor() {
}
void init(int max) {
void init(long max) {
_max = max;
}
virtual void step(int steps = 1) {
virtual void step(long steps = 1l) {
_steps += steps;
}

View File

@ -2,6 +2,7 @@
#include "core/Log.h"
#include "core/App.h"
#include "io/Filesystem.h"
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
namespace video {

View File

@ -11,12 +11,5 @@ fips_begin_module(voxel)
fips_deps(core io noise zlib ${SDL2_LIBRARIES})
fips_end_module()
gtest_begin(voxel)
fips_dir(tests)
fips_files(
WorldTest.cpp
)
find_package(SDL2 REQUIRED)
include_directories(${SDL2_INCLUDE_DIRS})
fips_deps(voxel ${SDL2_LIBRARIES})
gtest_end()
gtest_suite_files(tests tests/WorldTest.cpp)
gtest_suite_deps(tests voxel)

View File

@ -3,6 +3,7 @@
#include <PolyVox/MaterialDensityPair.h>
#include <PolyVox/Mesh.h>
#include <PolyVox/Vertex.h>
#include <PolyVox/CubicSurfaceExtractor.h>
#include <glm/glm.hpp>
#include <vector>

View File

@ -8,6 +8,8 @@
#include "io/File.h"
#include "Raycast.h"
#include "Voxel.h"
#include "core/Random.h"
#include "noise/SimplexNoise.h"
#include <PolyVox/AStarPathfinder.h>
#include <PolyVox/CubicSurfaceExtractor.h>
#include <PolyVox/RawVolume.h>
@ -21,21 +23,30 @@ namespace voxel {
#define MAX_HEIGHT 256
#define WORLD_FILE_VERSION 1
void World::Pager::pageIn(const PolyVox::Region& region, WorldData::Chunk* chunk) {
if (!_world.load(region, chunk)) {
_world.create(region, chunk);
_world.save(region, chunk);
}
}
void World::Pager::pageOut(const PolyVox::Region& region, WorldData::Chunk* chunk) {
_world.save(region, chunk);
}
// http://code.google.com/p/fortressoverseer/source/browse/Overseer/PolyVoxGenerator.cpp
World::World() :
_volumeData(nullptr), _seed(0), _size(0), _noise(), _worldShapeNoise(), _threadPool(1), _rwLock("World") {
_chunkSize = core::Var::get("cl_chunksize", "16", core::CV_READONLY);
_pager(*this), _seed(0), _threadPool(1), _rwLock("World") {
_chunkSize = core::Var::get("cl_chunksize", "64", core::CV_READONLY);
_volumeData = new WorldData(&_pager, 256 * 1024 * 1024, _chunkSize->intVal());
}
World::~World() {
if (_volumeData)
delete _volumeData;
_volumeData = nullptr;
}
int World::internalFindFloor(int x, int z) const {
for (int i = MAX_HEIGHT - 1; i >= 0; i--) {
const int material = getMaterial(x, i, z);
int World::findChunkFloor(int chunkSize, WorldData::Chunk* chunk, int x, int z) {
for (int i = chunkSize - 1; i >= 0; i--) {
const int material = chunk->getVoxel(x, i, z).getMaterial();
if (material != AIR && material != CLOUDS) {
return i + 1;
}
@ -43,25 +54,16 @@ int World::internalFindFloor(int x, int z) const {
return -1;
}
glm::ivec3 World::internalRandomPos(int border) const {
const glm::ivec2& pos = randomPosWithoutHeight(border);
const int y = internalFindFloor(pos.x, pos.y);
return glm::ivec3(pos.x, y, pos.y);
}
glm::ivec2 World::randomPosWithoutHeight(int border) const {
if (_size <= 2 * border) {
Log::warn("Border %i exceeds size %i", border, _size);
return glm::ivec2(0);
}
std::uniform_int_distribution<int> distribution(border, _size - border);
const int x = distribution(_engine);
const int z = distribution(_engine);
glm::ivec2 World::randomPosWithoutHeight(const PolyVox::Region& region) {
std::uniform_int_distribution<int> distributionX(region.getLowerX(), region.getUpperX());
std::uniform_int_distribution<int> distributionZ(region.getLowerZ(), region.getUpperZ());
const int x = distributionX(_engine);
const int z = distributionZ(_engine);
return glm::ivec2(x, z);
}
glm::ivec3 World::randomPos(int border) const {
const glm::ivec2& pos = randomPosWithoutHeight(border);
glm::ivec3 World::randomPos() const {
const glm::ivec2 pos(0, 0);
const int y = findFloor(pos.x, pos.y);
return glm::ivec3(pos.x, y, pos.y);
}
@ -70,15 +72,10 @@ glm::ivec3 World::randomPos(int border) const {
// The surface extractor outputs the mesh in an efficient compressed format which
// is not directly suitable for rendering.
void World::scheduleMeshExtraction(const glm::ivec2& p) {
if (p.x < 0 || p.y < 0 || p.x >= _size || p.y >= _size) {
Log::debug("skip mesh extraction for %i:%i (%i)", p.x, p.y, _size);
return;
}
const int size = _chunkSize->intVal();
const glm::ivec2& pos = getGridPos(p);
if (_meshesExtracted.find(pos) != _meshesExtracted.end()) {
Log::debug("mesh is already extracted for %i:%i (%i)", p.x, p.y, _size);
Log::debug("mesh is already extracted for %i:%i", p.x, p.y);
return;
}
_meshesExtracted.insert(pos);
@ -91,8 +88,9 @@ void World::scheduleMeshExtraction(const glm::ivec2& p) {
const PolyVox::Region region(mins, maxs);
DecodedMeshData data;
{
std::lock_guard<std::mutex> lock(_mutex);
data.mesh = PolyVox::decodeMesh(PolyVox::extractCubicMesh(_volumeData, region));
locked([&] () {
data.mesh = PolyVox::decodeMesh(PolyVox::extractCubicMesh(_volumeData, region));
});
}
data.translation = pos;
@ -102,8 +100,13 @@ void World::scheduleMeshExtraction(const glm::ivec2& p) {
}
int World::findFloor(int x, int z) const {
std::lock_guard<std::mutex> lock(_mutex);
return internalFindFloor(x, z);
for (int i = MAX_HEIGHT - 1; i >= 0; i--) {
const int material = getMaterial(x, i, z);
if (material != AIR && material != CLOUDS) {
return i + 1;
}
}
return -1;
}
void World::allowReExtraction(const glm::ivec2& pos) {
@ -111,196 +114,210 @@ void World::allowReExtraction(const glm::ivec2& pos) {
}
World::Result World::raycast(const glm::vec3& start, const glm::vec3& end, voxel::Raycast& raycast) {
if (_volumeData == nullptr)
return World::FAILED;
std::lock_guard<std::mutex> lock(_mutex);
PolyVox::RaycastResult result = PolyVox::raycastWithEndpoints(_volumeData, PolyVox::Vector3DFloat(start.x, start.y, start.z),
PolyVox::Vector3DFloat(end.x, end.y, end.z), raycast);
if (result == PolyVox::RaycastResults::Completed)
return World::COMPLETED;
return World::INTERUPTED;
return locked([&] () {
PolyVox::RaycastResult result = PolyVox::raycastWithEndpoints(_volumeData, PolyVox::Vector3DFloat(start.x, start.y, start.z),
PolyVox::Vector3DFloat(end.x, end.y, end.z), raycast);
if (result == PolyVox::RaycastResults::Completed)
return World::COMPLETED;
return World::INTERUPTED;
});
}
bool World::findPath(const PolyVox::Vector3DInt32& start, const PolyVox::Vector3DInt32& end,
std::list<PolyVox::Vector3DInt32>& listResult) {
if (_volumeData == nullptr)
return false;
static auto f = [] (const voxel::WorldData* volData, const PolyVox::Vector3DInt32& v3dPos) {
voxel::Voxel voxel = volData->getVoxel(v3dPos);
return voxel.getDensity() != 0;
};
std::lock_guard<std::mutex> lock(_mutex);
const PolyVox::AStarPathfinderParams<voxel::WorldData> params(_volumeData, start, end, &listResult, 1.0f, 10000,
PolyVox::TwentySixConnected, std::bind(f, std::placeholders::_1, std::placeholders::_2));
PolyVox::AStarPathfinder<voxel::WorldData> pf(params);
// TODO: move into threadpool
pf.execute();
locked([&] () {
const PolyVox::AStarPathfinderParams<voxel::WorldData> params(_volumeData, start, end, &listResult, 1.0f, 10000,
PolyVox::TwentySixConnected, std::bind(f, std::placeholders::_1, std::placeholders::_2));
PolyVox::AStarPathfinder<voxel::WorldData> pf(params);
// TODO: move into threadpool
pf.execute();
});
return true;
}
void World::destroy() {
if (!_volumeData)
return;
std::lock_guard<std::mutex> lock(_mutex);
if (!_volumeData)
return;
Log::info("flush the world");
delete _volumeData;
_volumeData = nullptr;
locked([this] () {
_volumeData->flushAll();
_seed = 0l;
Log::info("flush the world");
});
}
void World::createCirclePlane(const glm::ivec3& center, int width, int depth, double radius, const Voxel& voxel) {
const double xRadius = (width - 1) / 2.0;
const double zRadius = (depth - 1) / 2.0;
void World::createCirclePlane(const PolyVox::Region& region, WorldData::Chunk* chunk, const glm::ivec3& center, int width, int depth, double radius, const Voxel& voxel) {
const int xRadius = width / 2;
const int zRadius = depth / 2;
const double minRadius = std::min(xRadius, zRadius);
const double xRatio = xRadius / (float) minRadius;
const double zRatio = zRadius / (float) minRadius;
const double ratioX = xRadius / minRadius;
const double ratioZ = zRadius / minRadius;
for (double z = -zRadius; z <= zRadius; ++z) {
for (double x = -xRadius; x <= xRadius; ++x) {
const double xP = x / xRatio;
const double zP = z / zRatio;
const double distance = sqrt(pow(xP, 2) + pow(zP, 2));
if (distance < radius) {
if (_volumeData->getEnclosingRegion().containsPoint(center.x + x, center.y, center.z + z))
_volumeData->setVoxel(center.x + x, center.y, center.z + z, voxel);
for (int z = -zRadius; z <= zRadius; ++z) {
for (int x = -xRadius; x <= xRadius; ++x) {
const double distance = glm::sqrt(glm::pow(x / ratioX, 2.0) + glm::pow(z / ratioZ, 2.0));
if (distance > radius) {
continue;
}
chunk->setVoxel(center.x + x, center.y, center.z + z, voxel);
}
}
}
void World::createCube(const PolyVox::Region& region, WorldData::Chunk* chunk, const glm::ivec3& pos, int width, int height, int depth, const Voxel& voxel) {
const int w = width / 2;
const int h = height / 2;
const int d = depth / 2;
for (int x = -w; x < width - w; ++x) {
for (int y = -h; y < height - h; ++y) {
for (int z = -d; z < depth - d; ++z) {
chunk->setVoxel(pos.x + x, pos.y + y, pos.z + z, voxel);
}
}
}
}
void World::createCube(const glm::ivec3& pos, int width, int height, int depth, const Voxel& voxel) {
for (int x = 0; x < width; ++x) {
for (int y = 0; y < height; ++y) {
for (int z = 0; z < depth; ++z) {
if (_volumeData->getEnclosingRegion().containsPoint(pos.x + x, pos.y + y, pos.z + z)) {
_volumeData->setVoxel(pos.x + x, pos.y + y, pos.z + z, voxel);
}
}
}
}
void World::createPlane(const PolyVox::Region& region, WorldData::Chunk* chunk, const glm::ivec3& pos, int width, int depth, const Voxel& voxel) {
createCube(region, chunk, pos, width, 1, depth, voxel);
}
void World::createPlane(const glm::ivec3& pos, int width, int depth, const Voxel& voxel) {
createCube(pos, width, 1, depth, voxel);
}
void World::createEllipse(const glm::ivec3& pos, int width, int height, int depth, const Voxel& voxel) {
const double heightRadius = (height - 1.0) / 2.0;
void World::createEllipse(const PolyVox::Region& region, WorldData::Chunk* chunk, const glm::ivec3& pos, int width, int height, int depth, const Voxel& voxel) {
const int heightLow = height / 2;
const int heightHigh = height - heightLow;
const double minDimension = std::min(width, depth);
const double adjustedMinRadius = (minDimension - 1.0) / 2.0;
const double heightFactor = heightRadius / adjustedMinRadius;
for (double y = -heightRadius; y <= heightRadius; ++y) {
const double adjustedHeight = abs(y / heightFactor);
const double circleRadius = sqrt(pow(adjustedMinRadius + 0.5, 2.0) - pow(adjustedHeight, 2.0));
const double adjustedMinRadius = minDimension / 2.0;
const double heightFactor = heightLow / adjustedMinRadius;
for (int y = -heightLow; y <= heightHigh; ++y) {
const double percent = glm::abs(y / heightFactor);
const double circleRadius = glm::pow(adjustedMinRadius + 0.5, 2.0) - glm::pow(percent, 2.0);
const glm::ivec3 planePos(pos.x, pos.y + y, pos.z);
createCirclePlane(planePos, width, depth, circleRadius, voxel);
createCirclePlane(region, chunk, planePos, width, depth, circleRadius, voxel);
}
}
void World::createCone(const glm::ivec3& pos, int width, int height, int depth, const Voxel& voxel) {
const double heightRadius = height - 0.5;
void World::createCone(const PolyVox::Region& region, WorldData::Chunk* chunk, const glm::ivec3& pos, int width, int height, int depth, const Voxel& voxel) {
const int heightLow = height / 2;
const int heightHigh = height - heightLow;
const double minDimension = std::min(width, depth);
const double minRadius = minDimension / 2.0;
for (double y = 0.5; y <= heightRadius; y++) {
const double percent = 1 - (y / height);
const double circleRadius = percent * minRadius;
for (int y = -heightLow; y <= heightHigh; ++y) {
const double percent = 1.0 - ((y + heightLow) / double(height));
const double circleRadius = glm::pow(percent * minRadius, 2.0);
const glm::ivec3 planePos(pos.x, pos.y + y, pos.z);
createCirclePlane(planePos, width, depth, circleRadius, voxel);
createCirclePlane(region, chunk, planePos, width, depth, circleRadius, voxel);
}
}
void World::createDome(const glm::ivec3& pos, int width, int height, int depth, const Voxel& voxel) {
// TODO:
void World::createDome(const PolyVox::Region& region, WorldData::Chunk* chunk, const glm::ivec3& pos, int width, int height, int depth, const Voxel& voxel) {
const int heightLow = height / 2;
const int heightHigh = height - heightLow;
const double minDimension = std::min(width, depth);
const double minRadius = minDimension / 2.0;
const double heightFactor = height / (minDimension - 1.0) / 2.0;
for (int y = -heightLow; y <= heightHigh; ++y) {
const double percent = glm::abs((y + heightLow) / heightFactor);
const double circleRadius = glm::pow(minRadius, 2.0) - glm::pow(percent, 2.0);
const glm::ivec3 planePos(pos.x, pos.y + y, pos.z);
createCirclePlane(region, chunk, planePos, width, depth, circleRadius, voxel);
}
}
void World::addTree(const glm::ivec3& pos, TreeType type, int trunkHeight) {
void World::addTree(int chunkSize, const PolyVox::Region& region, WorldData::Chunk* chunk, const glm::ivec3& pos, TreeType type, int trunkHeight, int trunkWidth, int width, int depth, int height) {
const int top = (int) pos.y + trunkHeight;
const int sizeX = _volumeData->getWidth();
const int sizeY = _volumeData->getDepth();
const int sizeZ = _volumeData->getHeight();
const Voxel voxel(TRUNK, Voxel::getMaxDensity());
Log::trace("generate tree at %i:%i:%i", pos.x, pos.y, pos.z);
for (int y = pos.y; y < top; ++y) {
const int width = std::max(1, 3 - (y - pos.y));
for (int x = pos.x - width; x < pos.x + width; ++x) {
for (int z = pos.z - width; z < pos.z + width; ++z) {
if ((x >= pos.x + 1 || x < pos.x - 1) && (z >= pos.z + 1 || z < pos.z - 1))
const int trunkWidthY = trunkWidth + std::max(0, 2 - (y - pos.y));
for (int x = pos.x - trunkWidthY; x < pos.x + trunkWidthY; ++x) {
for (int z = pos.z - trunkWidthY; z < pos.z + trunkWidthY; ++z) {
if ((x >= pos.x + trunkWidthY || x < pos.x - trunkWidthY) && (z >= pos.z + trunkWidthY || z < pos.z - trunkWidthY)) {
continue;
if (x < 0 || y < 0 || z < 0 || x >= sizeX || y >= sizeY || z >= sizeZ)
continue;
_volumeData->setVoxel(x, y, z, voxel);
}
if (y == pos.y) {
y = findChunkFloor(chunkSize, chunk, x, z);
}
chunk->setVoxel(x, y, z, voxel);
}
}
}
const int width = 16;
const int depth = 16;
const int height = 12;
const Voxel leavesVoxel(LEAVES, 1);
const glm::ivec3 leafesPos(pos.x, top + height / 2, pos.z);
if (type == TreeType::ELLIPSIS) {
const int centerLeavesPos = top + height / 2;
const glm::ivec3 leafesPos(pos.x, centerLeavesPos, pos.z);
createEllipse(leafesPos, width, height, depth, leavesVoxel);
createEllipse(region, chunk, leafesPos, width, height, depth, leavesVoxel);
} else if (type == TreeType::CONE) {
const glm::ivec3 leafesPos(pos.x, top, pos.z);
createCone(leafesPos, width, height, depth, leavesVoxel);
createCone(region, chunk, leafesPos, width, height, depth, leavesVoxel);
} else if (type == TreeType::DOME) {
const glm::ivec3 leafesPos(pos.x, top, pos.z);
createDome(leafesPos, width, height, depth, leavesVoxel);
createDome(region, chunk, leafesPos, width, height, depth, leavesVoxel);
} else if (type == TreeType::CUBE) {
createCube(region, chunk, leafesPos, width, height, depth, leavesVoxel);
// TODO: use CreatePlane
createCube(region, chunk, leafesPos, width + 2, height - 2, depth - 2, leavesVoxel);
createCube(region, chunk, leafesPos, width - 2, height + 2, depth - 2, leavesVoxel);
createCube(region, chunk, leafesPos, width - 2, height - 2, depth + 2, leavesVoxel);
}
}
void World::createTrees() {
const int amount = 4;
Log::debug("generate %i trees", amount);
// TODO: don't place trees on top of other trees
// TODO: don't use random positions, but decide on the material of some positions
for (int i = 0; i < amount; ++i) {
const glm::ivec3& pos = internalRandomPos(10);
addTree(pos, TreeType::ELLIPSIS);
}
for (int i = 0; i < amount; ++i) {
const glm::ivec3& pos = internalRandomPos(10);
addTree(pos, TreeType::CONE);
void World::createTrees(int chunkSize, const PolyVox::Region& region, WorldData::Chunk* chunk) {
for (int i = 0; i < 5; ++i) {
const int maxSize = 14;
const int rndVal = core::random(maxSize, chunkSize - maxSize);
// number should be even
if (!(rndVal % 2)) {
continue;
}
const int rndValZ = core::random(maxSize, chunkSize - maxSize);
// TODO: use a noise map to get the position
glm::ivec3 pos(rndVal, -1, rndValZ);
const int y = findChunkFloor(chunkSize, chunk, pos.x, pos.z);
if (y < 0) {
continue;
}
pos.y = y;
const int size = core::random(12, maxSize);
const int height = core::random(10, 14);
const int trunkHeight = core::random(5, 9);
const int treeType = core::random(0, int(TreeType::MAX) - 1);
const int trunkWidth = 1;
addTree(chunkSize, region, chunk, pos, (TreeType)treeType, trunkHeight, trunkWidth, size, size, height);
}
}
void World::createClouds() {
void World::createClouds(const PolyVox::Region& region, WorldData::Chunk* chunk) {
const int amount = 4;
Log::debug("generate %i clouds", amount);
const Voxel voxel(CLOUDS, Voxel::getMinDensity());
for (int i = 0; i < amount; ++i) {
const int height = 10;
const glm::ivec2& pos = randomPosWithoutHeight();
glm::ivec3 cloudCenterPos(pos.x, _size - height, pos.y);
createEllipse(cloudCenterPos, 10, height, 10, voxel);
const glm::ivec2& pos = randomPosWithoutHeight(region);
glm::ivec3 cloudCenterPos(pos.x, region.getUpperY() - height, pos.y);
createEllipse(region, chunk, cloudCenterPos, 10, height, 10, voxel);
cloudCenterPos.x -= 5;
cloudCenterPos.y -= 5 + i;
createEllipse(cloudCenterPos, 20, height, 35, voxel);
createEllipse(region, chunk, cloudCenterPos, 20, height, 35, voxel);
}
}
void World::createUnderground() {
void World::createUnderground(const PolyVox::Region& region, WorldData::Chunk* chunk) {
glm::ivec3 startPos(1, 1, 1);
const Voxel voxel(DIRT, Voxel::getMaxDensity());
createPlane(startPos, 10, 10, voxel);
createPlane(region, chunk, startPos, 10, 10, voxel);
}
bool World::load(long seed, util::IProgressMonitor* progressMonitor) {
bool World::load(const PolyVox::Region& region, WorldData::Chunk* chunk) {
const core::App* app = core::App::getInstance();
const io::FilesystemPtr& filesystem = app->filesystem();
const std::string filename = "world-" + std::to_string(seed) + ".wld";
const std::string& filename = core::string::format("world-%li-%i-%i-%i.wld", _seed, region.getCentreX(), region.getCentreY(), region.getCentreZ());
const io::FilePtr& f = filesystem->open(filename);
if (!f)
if (!f->exists()) {
return false;
destroy();
}
Log::trace("Try to load world %s", f->getName().c_str());
uint8_t *fileBuf;
// TODO: load async, put world into state loading, and do the real loading in onFrame if the file is fully loaded
@ -315,31 +332,19 @@ bool World::load(long seed, util::IProgressMonitor* progressMonitor) {
bs.append(fileBuf, fileLen);
int len;
int version;
bs.readFormat("ibli", &len, &version, &_seed, &_size);
if (progressMonitor)
progressMonitor->init(_size * MAX_HEIGHT * _size);
bs.readFormat("ib", &len, &version);
if (version != WORLD_FILE_VERSION) {
Log::error("file %s has a wrong version number %i (expected %i)", f->getName().c_str(), version, WORLD_FILE_VERSION);
return false;
}
if (_seed != seed) {
Log::error("file %s has a wrong seed %li (expected %li)", f->getName().c_str(), _seed, seed);
return false;
}
if (_size <= 0) {
Log::error("file %s has invalid size: %i", f->getName().c_str(), _size);
return false;
}
const int sizeLimit = 256;
const int sizeLimit = 1024;
if (len > 1000l * 1000l * sizeLimit) {
Log::error("extracted memory would be more than %i MB for the file %s", sizeLimit, f->getName().c_str());
return false;
}
_engine.seed(_seed);
Log::info("Loading a world with size %i from file %s,uncompressing to %i", _size, f->getName().c_str(), (int) len);
Log::info("Loading a world from file %s,uncompressing to %i", f->getName().c_str(), (int) len);
uint8_t* targetBuf = new uint8_t[len];
std::unique_ptr<uint8_t[]> smartTargetBuf(targetBuf);
@ -354,8 +359,6 @@ bool World::load(long seed, util::IProgressMonitor* progressMonitor) {
core::ByteStream voxelBuf(len);
voxelBuf.append(targetBuf, len);
const PolyVox::Region region(0, 0, 0, _size, MAX_HEIGHT, _size);
WorldData* volumeData = new WorldData(region);
const PolyVox::Vector3DInt32& lower = region.getLowerCorner();
const PolyVox::Vector3DInt32& upper = region.getUpperCorner();
const int lowerZ = lower.getZ();
@ -371,29 +374,16 @@ bool World::load(long seed, util::IProgressMonitor* progressMonitor) {
const uint8_t material = voxelBuf.readByte();
const uint8_t density = voxelBuf.readByte();
const Voxel voxel(material, density);
volumeData->setVoxel(x, y, z, voxel);
if (progressMonitor)
progressMonitor->step();
_volumeData->setVoxel(x, y, z, voxel);
}
}
}
if (progressMonitor)
progressMonitor->done();
_volumeData = volumeData;
return true;
}
bool World::save(long seed) {
if (_volumeData == nullptr) {
Log::error("No world created yet");
return false;
}
if (seed != _seed) {
Log::error("Seeds don't match");
return false;
}
bool World::save(const PolyVox::Region& region, WorldData::Chunk* chunk) {
Log::info("Save chunk");
core::ByteStream voxelStream;
const PolyVox::Region region(0, 0, 0, _size, MAX_HEIGHT, _size);
const PolyVox::Vector3DInt32& lower = region.getLowerCorner();
const PolyVox::Vector3DInt32& upper = region.getUpperCorner();
const int lowerZ = lower.getZ();
@ -413,7 +403,7 @@ bool World::save(long seed) {
}
// save the stuff
const std::string filename = "world-" + std::to_string(seed) + ".wld";
const std::string filename = core::string::format("world-%li-%i-%i-%i.wld", _seed, region.getCentreX(), region.getCentreY(), region.getCentreZ());
const core::App* app = core::App::getInstance();
const io::FilesystemPtr& filesystem = app->filesystem();
@ -428,7 +418,7 @@ bool World::save(long seed) {
return false;
}
core::ByteStream final;
final.addFormat("ibli", voxelSize, WORLD_FILE_VERSION, _seed, _size);
final.addFormat("ib", voxelSize, WORLD_FILE_VERSION);
final.append(compressedVoxelBuf, neededVoxelBufLen);
if (!filesystem->write(filename, final.getBuffer(), final.getSize())) {
Log::error("Failed to write file %s", filename.c_str());
@ -438,52 +428,31 @@ bool World::save(long seed) {
return true;
}
void World::create(long seed, int size, util::IProgressMonitor* progressMonitor) {
if (_seed == seed)
return;
_seed = seed;
_engine.seed(_seed);
_size = size;
Log::info("Using seed %li with size %i", seed, size);
{
std::lock_guard<std::mutex> lock(_mutex);
if (_volumeData != nullptr) {
Log::info("Create a new world");
destroy();
}
_noise.setSeed(seed);
_noise.init();
const PolyVox::Region region(0, 0, 0, size - 1, MAX_HEIGHT - 1, size - 1);
WorldData* volumeData = new WorldData(region);
if (progressMonitor)
progressMonitor->init(size * MAX_HEIGHT * size + 2);
const PolyVox::Vector3DInt32& lower = region.getLowerCorner();
const PolyVox::Vector3DInt32& upper = region.getUpperCorner();
for (double z = lower.getZ(); z < upper.getZ(); ++z) {
for (double x = lower.getX(); x < upper.getX(); ++x) {
const int height = (_noise.get(x, z, 0, 256.0) + 1) * 128;
for (double h = 0; h <= height; ++h) {
if (progressMonitor) {
progressMonitor->step();
}
const Voxel voxel(DIRT, DIRT);
volumeData->setVoxel(x, h, z, voxel);
}
void World::create(const PolyVox::Region& region, WorldData::Chunk* chunk) {
Log::info("Create new chunk at %i:%i:%i", region.getCentreX(), region.getCentreY(), region.getCentreZ());
const int width = region.getWidthInVoxels();
const int depth = region.getDepthInVoxels();
const int height = region.getHeightInVoxels();
for (double z = 0; z < depth; ++z) {
for (double x = 0; x < width; ++x) {
const glm::vec2 noisePos2d = glm::vec2(x, z);
// TODO: include random here, too
const float landscapeNoise = noise::Simplex::Noise2D(noisePos2d, 3, 0.1f, 0.01f);
const float noiseNormalized = (landscapeNoise + 1.0f) * 0.5f;
const float mountainNoise = noise::Simplex::Noise2D(noisePos2d, 2, 0.3f, 0.00075f);
const float mountainNoiseNormalized = (mountainNoise + 1.0f) * 0.5f;
const float mountainMultiplier = mountainNoiseNormalized * (mountainNoiseNormalized + 0.5f);
const float n = glm::clamp(noiseNormalized * mountainMultiplier, 0.0f, 1.0f);
const int ni = n * height;
for (int h = 0; h <= ni; ++h) {
// TODO: use biommanager
const Voxel voxel(DIRT, DIRT);
chunk->setVoxel(x, h, z, voxel);
}
}
_volumeData = volumeData;
createTrees();
if (progressMonitor) {
progressMonitor->step();
}
createClouds();
if (progressMonitor) {
progressMonitor->step();
progressMonitor->done();
}
}
createTrees(_chunkSize->intVal(), region, chunk);
createClouds(region, chunk);
core::App::getInstance()->eventBus()->publish(WorldCreatedEvent(this));
}

View File

@ -7,20 +7,17 @@
#include <mutex>
#include <queue>
#include <random>
#include <chrono>
#include "noise/AccidentalNoise.h"
#include "noise/WorldShapeNoise.h"
#include "noise/NoisePPNoise.h"
#include "noise/PerlinNoise.h"
#include "io/Filesystem.h"
#include "WorldData.h"
#include "WorldEvents.h"
#include "Voxel.h"
#include "Raycast.h"
#include "util/IProgressMonitor.h"
#include "core/ThreadPool.h"
#include "core/ReadWriteLock.h"
#include "core/Var.h"
#include "core/Log.h"
namespace voxel {
@ -35,15 +32,8 @@ public:
World();
~World();
void create(long seed, int size, util::IProgressMonitor* progressMonitor = nullptr);
bool load(long seed, util::IProgressMonitor* progressMonitor = nullptr);
bool save(long seed);
void destroy();
inline bool isCreated() const {
return _volumeData != nullptr;
}
Result raycast(const glm::vec3& start, const glm::vec3& end, voxel::Raycast& raycast);
bool findPath(const PolyVox::Vector3DInt32& start, const PolyVox::Vector3DInt32& end, std::list<PolyVox::Vector3DInt32>& listResult);
int findFloor(int x, int z) const;
@ -52,8 +42,7 @@ public:
/**
* @brief Returns a random position inside the boundaries of the world (on the surface)
*/
glm::ivec3 randomPos(int border = 0) const;
glm::ivec2 randomPosWithoutHeight(int border = 0) const;
glm::ivec3 randomPos() const;
/**
* @brief Cuts the given world coordinate down to mesh tile vectors
@ -98,22 +87,44 @@ public:
void onFrame(long dt);
inline int size() const { return _size; }
inline long seed() const { return _seed; }
void setSeed(long seed) {
Log::info("Seed is: %li", seed);
_seed = seed;
_engine.seed(seed);
}
inline bool isCreated() const {
return _seed != 0;
}
private:
enum class TreeType {
DOME,
CONE,
ELLIPSIS
ELLIPSIS,
CUBE,
MAX
};
WorldData* _volumeData;
class Pager: public WorldData::Pager {
private:
World& _world;
public:
Pager(World& world) :
_world(world) {
}
void pageIn(const PolyVox::Region& region, WorldData::Chunk* chunk) override;
void pageOut(const PolyVox::Region& region, WorldData::Chunk* chunk) override;
};
Pager _pager;
WorldData *_volumeData;
mutable std::mt19937 _engine;
long _seed;
int _size;
noise::NoisePPNoise _noise;
noise::WorldShapeNoise _worldShapeNoise;
struct IVec2HashEquals {
size_t operator()(const glm::ivec2& k) const {
@ -126,24 +137,50 @@ private:
}
};
// assumes that the mutex is already locked
glm::ivec3 internalRandomPos(int border = 0) const;
int internalFindFloor(int x, int y) const;
template<typename Func>
inline auto locked(Func&& func) -> typename std::result_of<Func()>::type {
if (_mutex.try_lock_for(std::chrono::milliseconds(5000))) {
lockGuard lock(_mutex, std::adopt_lock_t());
return func();
}
Log::warn("Most likely a deadlock in the world - execute without locking");
return func();
}
void createCirclePlane(const glm::ivec3& center, int width, int depth, double radius, const Voxel& voxel);
void createEllipse(const glm::ivec3& pos, int width, int height, int depth, const Voxel& voxel);
void createCone(const glm::ivec3& pos, int width, int height, int depth, const Voxel& voxel);
void createDome(const glm::ivec3& pos, int width, int height, int depth, const Voxel& voxel);
void createCube(const glm::ivec3& pos, int width, int height, int depth, const Voxel& voxel);
void createPlane(const glm::ivec3& pos, int width, int depth, const Voxel& voxel);
template<typename Func>
inline auto locked(Func&& func) const -> typename std::result_of<Func()>::type {
if (_mutex.try_lock_for(std::chrono::milliseconds(5000))) {
lockGuard lock(_mutex, std::adopt_lock_t());
return func();
}
Log::warn("Most likely a deadlock in the world - execute without locking");
return func();
}
void addTree(const glm::ivec3& pos, TreeType type, int trunkHeight = 10);
void createTrees();
void createClouds();
void createUnderground();
static int findChunkFloor(int chunkSize, WorldData::Chunk* chunk, int x, int y);
bool load(const PolyVox::Region& region, WorldData::Chunk* chunk);
bool save(const PolyVox::Region& region, WorldData::Chunk* chunk);
// don't access the volume in anything that is called here
void create(const PolyVox::Region& region, WorldData::Chunk* chunk);
static void createCirclePlane(const PolyVox::Region& region, WorldData::Chunk* chunk, const glm::ivec3& center, int width, int depth, double radius, const Voxel& voxel);
static void createEllipse(const PolyVox::Region& region, WorldData::Chunk* chunk, const glm::ivec3& pos, int width, int height, int depth, const Voxel& voxel);
static void createCone(const PolyVox::Region& region, WorldData::Chunk* chunk, const glm::ivec3& pos, int width, int height, int depth, const Voxel& voxel);
static void createDome(const PolyVox::Region& region, WorldData::Chunk* chunk, const glm::ivec3& pos, int width, int height, int depth, const Voxel& voxel);
static void createCube(const PolyVox::Region& region, WorldData::Chunk* chunk, const glm::ivec3& pos, int width, int height, int depth, const Voxel& voxel);
static void createPlane(const PolyVox::Region& region, WorldData::Chunk* chunk, const glm::ivec3& pos, int width, int depth, const Voxel& voxel);
static void addTree(int chunkSize, const PolyVox::Region& region, WorldData::Chunk* chunk, const glm::ivec3& pos, TreeType type, int trunkHeight, int trunkWidth, int width, int depth, int height);
static void createTrees(int chunkSize, const PolyVox::Region& region, WorldData::Chunk* chunk);
glm::ivec2 randomPosWithoutHeight(const PolyVox::Region& region);
void createClouds(const PolyVox::Region& region, WorldData::Chunk* chunk);
static void createUnderground(const PolyVox::Region& region, WorldData::Chunk* chunk);
core::ThreadPool _threadPool;
mutable std::mutex _mutex;
using mutex = std::recursive_timed_mutex;
mutable mutex _mutex;
using lockGuard = std::lock_guard<mutex>;
core::ReadWriteLock _rwLock;
std::deque<DecodedMeshData> _meshQueue;
// fast lookup for positions that are already extracted and available in the _meshData vector

View File

@ -1,10 +1,10 @@
#pragma once
#include <PolyVox/RawVolume.h>
#include <PolyVox/PagedVolume.h>
#include "Voxel.h"
namespace voxel {
typedef PolyVox::RawVolume<Voxel> WorldData;
typedef PolyVox::PagedVolume<Voxel> WorldData;
}

View File

@ -2,35 +2,11 @@
#include "voxel/World.h"
namespace voxel {
namespace {
const long seed = 1L;
const int size = 32;
}
class WorldTest: public core::AbstractTest {
};
TEST_F(WorldTest, testCreateSaveLoad) {
World world;
world.create(seed, size);
ASSERT_TRUE(world.isCreated());
ASSERT_EQ(seed, world.seed());
ASSERT_EQ(size, world.size());
ASSERT_TRUE(world.save(seed));
ASSERT_EQ(seed, world.seed());
ASSERT_EQ(size, world.size());
ASSERT_TRUE(world.load(seed));
ASSERT_TRUE(world.isCreated());
ASSERT_EQ(seed, world.seed());
ASSERT_EQ(size, world.size());
}
TEST_F(WorldTest, testLoad) {
World world;
ASSERT_TRUE(world.load(seed));
ASSERT_TRUE(world.isCreated());
ASSERT_EQ(seed, world.seed());
ASSERT_EQ(size, world.size());
TEST_F(WorldTest, testEmptyFillMe) {
}
}

View File

@ -33,7 +33,7 @@ core::AppState Server::onInit() {
Log::error("Failed to bind the server socket on %s:%i", host->strVal().c_str(), port->intVal());
return core::Cleanup;
}
Log::error("Server socket is up at %s:%i", host->strVal().c_str(), port->intVal());
Log::info("Server socket is up at %s:%i", host->strVal().c_str(), port->intVal());
return state;
}

View File

@ -1,14 +0,0 @@
fips_begin_app(worldgenerator cmdline)
fips_files(
WorldGenerator.cpp WorldGenerator.h
)
fips_dir(sauce)
fips_files(
WorldGeneratorModule.h
WorldGeneratorInjector.h
)
fips_deps(voxel core io)
copy_data_files(worldgenerator)
fips_end_app()

View File

@ -1,57 +0,0 @@
#include "WorldGenerator.h"
#include "sauce/WorldGeneratorInjector.h"
#include "core/Var.h"
WorldGenerator::WorldGenerator(voxel::WorldPtr world, core::EventBusPtr eventBus, core::TimeProviderPtr timeProvider, io::FilesystemPtr filesystem) :
App(filesystem, eventBus, 15681), _world(world), _timeProvider(timeProvider), _seed(0L), _size(0) {
init("engine", "worldgenerator");
}
core::AppState WorldGenerator::onInit() {
core::AppState state = App::onInit();
const core::VarPtr& seed = core::Var::get("seed");
const core::VarPtr& size = core::Var::get("size");
if (size->strVal().empty()) {
Log::error("No size specified: -set size <size>");
return core::AppState::Cleanup;
} else if (seed->strVal().empty()) {
Log::error("No seed specified: -set seed <seed>");
return core::AppState::Cleanup;
}
_seed = seed->longVal();
_size = size->intVal();
return state;
}
core::AppState WorldGenerator::onRunning() {
core::AppState state = core::App::onRunning();
class ProgressMonitor: public util::IProgressMonitor {
public:
void step(int steps = 1) override {
IProgressMonitor::step(steps);
Log::info("max: %i, steps: %i => %f\r", _max, _steps, progress());
}
void done() override {
Log::info("\ndone");
}
} monitor;
unsigned long start = _timeProvider->currentTime();
_world->create(_seed, _size, &monitor);
if (!_world->save(_seed)) {
Log::error("Failed to save the world for seed %li", _seed);
} else {
Log::info("World for seed %li created", _seed);
}
unsigned long end = _timeProvider->currentTime();
unsigned long delta = end - start;
Log::info("World generating process took %lu milliseconds", delta);
return state;
}
int main(int argc, char *argv[]) {
getInjector()->get<WorldGenerator>()->startMainLoop(argc, argv);
return EXIT_SUCCESS;
}

View File

@ -1,18 +0,0 @@
#pragma once
#include "core/App.h"
#include "voxel/World.h"
#include "core/TimeProvider.h"
class WorldGenerator: public core::App {
private:
voxel::WorldPtr _world;
core::TimeProviderPtr _timeProvider;
long _seed;
int _size;
public:
WorldGenerator(voxel::WorldPtr world, core::EventBusPtr eventBus, core::TimeProviderPtr timeProvider, io::FilesystemPtr filesystem);
core::AppState onInit() override;
core::AppState onRunning() override;
};

View File

@ -1,9 +0,0 @@
#pragma once
#include "WorldGeneratorModule.h"
inline sauce::shared_ptr<sauce::Injector> getInjector() {
sauce::Modules modules;
modules.add<WorldGeneratorModule>();
return modules.createInjector();
}

View File

@ -1,12 +0,0 @@
#pragma once
#include "core/AbstractModule.h"
#include "voxel/World.h"
class WorldGeneratorModule: public core::AbstractModule {
void configure() const override {
core::AbstractModule::configure();
bind<WorldGenerator>().in<sauce::SingletonScope>().to<WorldGenerator(voxel::World &, core::EventBus &, core::TimeProvider &, io::Filesystem &)>();
bind<voxel::World>().in<sauce::SingletonScope>().to<voxel::World>();
}
};