AssetManager/AssetCache system now mostly in place. AssetCache(s) create/read from a .CACHE file in the target directory. This file is human-readable and is the index for the cache. Each line contains the file's path, its checksum, and the filesize. This file is updated when the filesize of the file changes. Currently new files are found and added on start - to be added is a refresh function. Also to be added is an update via modification time - this will require a new entry in the cache index file. A single-line version heading should also be added.
Data files are now loaded from the Cache - Mesh and Program build from passed buffers now. These are generally read from a loaded Asset. Next up: updating it to work on Linux, Android, iOS, and OS X. Also added a somewhat poor HashTable template implementation. Works okay, but might not be the best.master
parent
05a43ddcdb
commit
bc831f05b0
|
@ -117,6 +117,7 @@ xcopy /s /e /d /y "..\..\data" "$(OutDir)data\"</Command>
|
|||
<ClInclude Include="..\..\src\common.hpp" />
|
||||
<ClInclude Include="..\..\src\Core.hpp" />
|
||||
<ClInclude Include="..\..\src\fio.hpp" />
|
||||
<ClInclude Include="..\..\src\HashTable.hpp" />
|
||||
<ClInclude Include="..\..\src\Log.hpp" />
|
||||
<ClInclude Include="..\..\src\Mat4.hpp" />
|
||||
<ClInclude Include="..\..\src\Mesh.hpp" />
|
||||
|
|
|
@ -157,5 +157,8 @@
|
|||
<ClInclude Include="..\..\src\AssetCache.hpp">
|
||||
<Filter>Classes</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\HashTable.hpp">
|
||||
<Filter>Classes</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,10 @@
|
|||
data/shaders/fb_fs.100.glsl 0 259
|
||||
data/models/cube.obj 825768822 432
|
||||
data/shaders/fb_vs.100.glsl 0 177
|
||||
data/shaders/default_fs.100.glsl 0 244
|
||||
data/shaders/default_vs.100.glsl 0 334
|
||||
data/shaders/fb_fs.glsl 3713587946 233
|
||||
data/shaders/fb_vs.glsl 2722618720 177
|
||||
data/shaders/default_fs.glsl 1466891365 218
|
||||
data/shaders/default_vs.glsl 685822220 336
|
||||
data/models/chest.obj 870013261 4806
|
|
@ -4,12 +4,31 @@ Asset
|
|||
#include "Asset.hpp"
|
||||
#include <stdlib.h>
|
||||
Asset::Asset() {
|
||||
flags = 0;
|
||||
flags = IS_NULL;
|
||||
data = NULL;
|
||||
data_length = 0;
|
||||
data_checksum = 0;
|
||||
}
|
||||
Asset::~Asset() {
|
||||
if (flags & IS_LOADED) {
|
||||
free(data);
|
||||
}
|
||||
}
|
||||
std::string Asset::getName() {
|
||||
return filename;
|
||||
}
|
||||
char* Asset::getData() {
|
||||
return data;
|
||||
}
|
||||
off_t Asset::getDataLength() {
|
||||
return data_length;
|
||||
}
|
||||
uint32_t Asset::getChecksum() {
|
||||
return data_checksum;
|
||||
}
|
||||
void Asset::unloadData() {
|
||||
if (flags & IS_LOADED) {
|
||||
free(data);
|
||||
}
|
||||
flags &= ~IS_LOADED;
|
||||
}
|
|
@ -6,20 +6,28 @@ Our basic Asset class. This class exists as a container for data, but does not d
|
|||
#ifndef ASSET_HPP
|
||||
#define ASSET_HPP
|
||||
#include <string>
|
||||
#include "checksum.hpp"
|
||||
|
||||
class Asset {
|
||||
friend class AssetManager;
|
||||
friend class AssetCache;
|
||||
public:
|
||||
Asset();
|
||||
~Asset();
|
||||
enum FLAGS {
|
||||
IS_LOADED = (1 << 1) // indicates data is populated
|
||||
IS_NULL = (1 << 1), //
|
||||
IS_LOADED = (1 << 2) // indicates data is populated
|
||||
};
|
||||
std::string getName();
|
||||
char *getData();
|
||||
off_t getDataLength();
|
||||
uint32_t getChecksum();
|
||||
void unloadData();
|
||||
protected:
|
||||
unsigned int flags; // Our asset state flags
|
||||
std::string filename; // Our filename, relative to the assets directory
|
||||
std::string data_checksum; // Our full data checksum
|
||||
uint32_t data_checksum; // Our full data checksum
|
||||
char *data; // Our data, NULL if IS_LOADED is not set
|
||||
size_t data_length; // Our data's length, in bytes
|
||||
off_t data_length; // Our data's length, in bytes
|
||||
};
|
||||
#endif
|
|
@ -1,12 +1,57 @@
|
|||
#include "AssetCache.hpp"
|
||||
#include "Log.hpp"
|
||||
#include "checksum.hpp"
|
||||
#include "fio.hpp"
|
||||
#include <stdio.h>
|
||||
#if _WIN32
|
||||
#include <Windows.h>
|
||||
#include <Strsafe.h>
|
||||
#endif
|
||||
|
||||
/* ======== Construction and Destruction ======== */
|
||||
AssetCache::AssetCache() {
|
||||
//assets.resize(128);
|
||||
assets = new HashTable<Asset*>(128);
|
||||
assets->setnull(NULL);
|
||||
}
|
||||
AssetCache::AssetCache(const char *cache_file) {
|
||||
assets = new HashTable<Asset*>(128);
|
||||
assets->setnull(NULL);
|
||||
fromDir(cache_file);
|
||||
}
|
||||
AssetCache::~AssetCache() {
|
||||
HashEntry<Asset*> *entry;
|
||||
int i = 0;
|
||||
while ((entry = assets->iterate()) != NULL) {
|
||||
Asset *asset = (Asset*)(entry->data);
|
||||
assets->del(asset->filename.c_str());
|
||||
if (asset != NULL) delete asset;
|
||||
}
|
||||
}
|
||||
/* ======== Cache loading ======== */
|
||||
int AssetCache::fromFile(const char *cache_file) {
|
||||
int ret;
|
||||
FILE *file = asset_fopen(cache_file, "rb");
|
||||
if (file == NULL) {
|
||||
LOG(LOG_ERROR) << "Could not open cache file: " << cache_file;
|
||||
return 1;
|
||||
}
|
||||
char fpath[1024];
|
||||
uint32_t fsum = 0;
|
||||
size_t fsize = 0;
|
||||
#ifdef _WIN32
|
||||
while ((ret = fscanf_s(file, "%s %u %u\n", fpath, 1024, &fsum, &fsize)) != EOF) {
|
||||
#else
|
||||
while ((ret = fscanf(file, "%s %u %u\n", fpath, &fsum, &fsize)) != EOF) {
|
||||
#endif
|
||||
Asset *asset = new Asset();
|
||||
asset->filename.assign(fpath);
|
||||
asset->flags &= ~Asset::IS_NULL;
|
||||
asset->data_checksum = fsum;
|
||||
asset->data_length = fsize;
|
||||
assets->set(fpath, asset);
|
||||
LOG(LOG_DEBUG) << "opened " << fpath << " " << fsum;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* fromDir
|
||||
|
@ -14,14 +59,130 @@ This function will first attempt to load the .CACHE file contained in the given
|
|||
If not, the function will traverse the full hierarchy of the given directory and populate the cache accordingly.
|
||||
*/
|
||||
int AssetCache::fromDir(const char *dir) {
|
||||
char cachepath[MAX_PATH];
|
||||
#ifndef _WIN32
|
||||
sprintf(cachepath, "%s/.CACHE", dir);
|
||||
#else
|
||||
sprintf_s(cachepath, MAX_PATH, "%s/.CACHE", dir);
|
||||
#endif
|
||||
if (fromFile(cachepath) != 0) {
|
||||
LOG(LOG_INFO) << FUNC_NAME << ": " << cachepath << " does not exist";
|
||||
}
|
||||
directory.assign(dir);
|
||||
traverseDir_r(dir);
|
||||
// TODO: move this to a "genChecksum()" sort of function
|
||||
/*HashEntry<Asset*> *entry;
|
||||
while ((entry = assets->iterate()) != NULL) {
|
||||
Asset *asset = (Asset*)(entry->data);
|
||||
// update the checksum if it doesn't exist yet.
|
||||
if (asset->data_checksum == 0) {
|
||||
char *data_buffer = NULL;
|
||||
size_t data_len = asset_fileToMem(asset->filename.c_str(), &(data_buffer));
|
||||
asset->data_checksum = crc32(1337, data_buffer, data_len);
|
||||
if (data_buffer != NULL) free(data_buffer);
|
||||
}
|
||||
} */
|
||||
return 0;
|
||||
}
|
||||
int AssetCache::traverseDir_r(const char *dir) {
|
||||
size_t dir_len = strlen(dir)+1;
|
||||
#if _WIN32
|
||||
WIN32_FIND_DATA ffd;
|
||||
HANDLE h_find = INVALID_HANDLE_VALUE;
|
||||
char wdir[MAX_PATH];
|
||||
|
||||
StringCbCopyN(wdir, MAX_PATH, dir, dir_len);
|
||||
StringCbCatN(wdir, MAX_PATH, "/*", 2);
|
||||
|
||||
if ((h_find = FindFirstFile(wdir, &ffd)) == INVALID_HANDLE_VALUE) {
|
||||
LOG(LOG_ERROR) << FUNC_NAME << " " << wdir << " FindFirstFile error " << GetLastError();
|
||||
return 1;
|
||||
}
|
||||
do {
|
||||
if (strcmp(ffd.cFileName, "..") == 0 || strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, ".CACHE") == 0) continue;
|
||||
char fpath[MAX_PATH];
|
||||
StringCbCopyN(fpath, MAX_PATH, dir, dir_len);
|
||||
StringCbCatN(fpath, MAX_PATH, "/", 2);
|
||||
StringCbCatN(fpath, MAX_PATH, ffd.cFileName, strlen(ffd.cFileName)+1);
|
||||
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
traverseDir_r(fpath);
|
||||
} else {
|
||||
Asset *asset = NULL;
|
||||
if (assets->exists(fpath)) {
|
||||
asset = assets->get(fpath);
|
||||
if (ffd.nFileSizeLow != asset->data_length) {
|
||||
LOG(LOG_DEBUG) << " file " << fpath << " changed from " << asset->data_length << " to " << ffd.nFileSizeLow;
|
||||
asset->data_length = ffd.nFileSizeLow;
|
||||
// TODO: recalculate checksum?
|
||||
}
|
||||
} else {
|
||||
asset = new Asset();
|
||||
asset->filename.assign(fpath);
|
||||
asset->flags &= ~Asset::IS_NULL;
|
||||
// FIXME: this is not the right way to do this. We also should use nFileSizeHigh. Also, this does not match the file's actual bytes
|
||||
asset->data_length = ffd.nFileSizeLow;
|
||||
assets->set(fpath, asset);
|
||||
}
|
||||
}
|
||||
} while (FindNextFile(h_find, &ffd));
|
||||
FindClose(h_find);
|
||||
#else
|
||||
struct dirent **files;
|
||||
struct stat file_stat;
|
||||
int n, i;
|
||||
char udir[MAX_PATH];
|
||||
char filepath[MAX_PATH];
|
||||
n = scandir(dir, &files, NULL, alphasort);
|
||||
if (n < 0) {
|
||||
LOG(LOG_INFO) << FUNC_NAME << ": No files in dir " << dir;
|
||||
return 2;
|
||||
}
|
||||
for (i = 0; i < n; i++) {
|
||||
snprintf(filepath, "%s/%s", dir, files[i]->dname);
|
||||
stat(filepath, &file_stat);
|
||||
switch(file_stat.st_mode & S_IFMT) {
|
||||
case S_IFDIR:
|
||||
traverseDir_r(filepath);
|
||||
break;
|
||||
case S_IFREG:
|
||||
Asset *asset = assets->get(fpath);
|
||||
if (asset == NULL) {
|
||||
asset = new Asset();
|
||||
asset->filename.assign(fpath);
|
||||
}
|
||||
asset->flags &= ~Asset::IS_NULL;
|
||||
assets->set(fpath, asset);
|
||||
break;
|
||||
case S_IFLNK:
|
||||
// TODO: travelDir_r if dir?
|
||||
break;
|
||||
default:
|
||||
// TODO: dunno
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* ======== Cache saving ======== */
|
||||
/* saveTo
|
||||
/* toFile
|
||||
This function saves the AssetCache to the given asset cache file
|
||||
*/
|
||||
int AssetCache::saveTo(const char *cache_file) {
|
||||
int AssetCache::toFile(const char *cache_file) {
|
||||
FILE *file = asset_fopen(cache_file, "wb");
|
||||
if (file == NULL) {
|
||||
LOG(LOG_ERROR) << FUNC_NAME << ": Could not open cache file: " << cache_file << " " << errno;
|
||||
return 1;
|
||||
}
|
||||
HashEntry<Asset*> *entry;
|
||||
int i = 0;
|
||||
while ((entry = assets->iterate()) != NULL) {
|
||||
Asset *asset = (Asset*)(entry->data);
|
||||
fprintf(file, "%s %u %u\n", asset->filename.c_str(), asset->data_checksum, asset->data_length);
|
||||
LOG(LOG_DEBUG) << ": saving " << asset->filename.c_str() << " bytes " << asset->data_length;
|
||||
}
|
||||
fclose(file);
|
||||
return 0;
|
||||
}
|
||||
/* ======== Cache updating and validation ======== */
|
||||
|
@ -43,13 +204,23 @@ This function attempts to return the given asset if it exists. If it does not ex
|
|||
The file _is not_ loaded.
|
||||
*/
|
||||
Asset* AssetCache::getAsset(const char *filename) {
|
||||
return NULL;
|
||||
Asset *asset = assets->get(filename);
|
||||
return asset;
|
||||
}
|
||||
/* loadAsset
|
||||
This function calls getAsset then loads the asset data into memory
|
||||
*/
|
||||
Asset* AssetCache::loadAsset(const char *filename) {
|
||||
return NULL;
|
||||
Asset *asset = getAsset(filename);
|
||||
if (asset == NULL) return NULL;
|
||||
if (!(asset->flags & Asset::IS_LOADED)) {
|
||||
asset->data_length = asset_fileToMem(asset->filename.c_str(), &(asset->data));
|
||||
asset->flags |= Asset::IS_LOADED;
|
||||
if (asset->data_checksum == 0) {
|
||||
asset->data_checksum = crc32(1337, asset->data, asset->data_length);
|
||||
}
|
||||
}
|
||||
return asset;
|
||||
}
|
||||
/* saveAsset
|
||||
This function saves the given asset's data to the corresponding data file
|
||||
|
|
|
@ -5,17 +5,20 @@ Asset Cache
|
|||
#ifndef ASSETCACHE_HPP
|
||||
#define ASSETCACHE_HPP
|
||||
#include "Asset.hpp"
|
||||
#include "HashTable.hpp"
|
||||
#include <vector>
|
||||
class AssetCache {
|
||||
friend class AssetManager;
|
||||
public:
|
||||
AssetCache();
|
||||
AssetCache(const char *cache_dir);
|
||||
~AssetCache();
|
||||
// load
|
||||
int fromFile(const char *cache_file); // load a cache file
|
||||
int fromDir(const char *dir); // create/load a cache from the given directory
|
||||
int traverseDir_r(const char *dir); // recursive function for adding Asset(s) from a given directory to the cache
|
||||
// save
|
||||
int saveTo(const char *cache_file); // saves the loaded cache to the given file
|
||||
int toFile(const char *cache_file); // saves the loaded cache to the given file
|
||||
// update
|
||||
int validateCache(); // checks if cache files are valid and exist
|
||||
int updateCache(); // scans directory for changes and updates the cache accordingly
|
||||
|
@ -25,7 +28,8 @@ class AssetCache {
|
|||
Asset *saveAsset(const char *filename); // saves the given asset to disk
|
||||
Asset *createAsset(const char *filename, const char *data, size_t data_len); // create a new Asset, populating it with given data
|
||||
protected:
|
||||
std::vector<Asset> asset; // TODO: replace with HashTable
|
||||
//std::vector<Asset> asset; // TODO: replace with HashTable
|
||||
HashTable<Asset*> *assets;
|
||||
std::string directory;
|
||||
};
|
||||
#ifdef __ANDROID__
|
||||
|
|
|
@ -19,21 +19,51 @@ Following this, the USER AssetCache is loaded, and are:
|
|||
#include "checksum.hpp"
|
||||
|
||||
AssetManager::AssetManager() {
|
||||
null_asset.filename = "NULL";
|
||||
}
|
||||
AssetManager::~AssetManager() {
|
||||
AssetCache *cache;
|
||||
int i;
|
||||
for (i = caches.size()-1; i >= 0; i--) {
|
||||
try {
|
||||
cache = caches.at(i);
|
||||
} catch (const std::out_of_range& oor) {
|
||||
LOG(LOG_ERROR) << FUNC_NAME << ": cache index out of range: " << oor.what();
|
||||
continue;
|
||||
}
|
||||
// FIXME: should we even save the caches from here?
|
||||
// save the cache to its proper CACHE file
|
||||
char cachepath[1024];
|
||||
#ifndef _WIN32
|
||||
sprintf(cachepath, "%s/.CACHE", cache->directory.c_str());
|
||||
#else
|
||||
sprintf_s(cachepath, 1024, "%s/.CACHE", cache->directory.c_str());
|
||||
#endif
|
||||
cache->toFile(cachepath);
|
||||
// annd delete!
|
||||
delete cache;
|
||||
}
|
||||
}
|
||||
/* ======== Asset Loading ======== */
|
||||
Asset* AssetManager::loadFile(const char *filename) {
|
||||
LOG(LOG_INFO) << FUNC_NAME << " " << filename << ": " << std::hex << crc32(1337, filename, strlen(filename));
|
||||
// read in the file and get its checksum
|
||||
char *buffer = NULL;
|
||||
size_t len = asset_fileToMem(filename, &buffer);
|
||||
if (len == 0) {
|
||||
LOG(LOG_ERROR) << FUNC_NAME << " read length was 0, could not load file " << filename;
|
||||
return NULL;
|
||||
Asset *asset = NULL;
|
||||
AssetCache *cache = NULL;
|
||||
int i;
|
||||
for (i = caches.size()-1; i >= 0; i--) {
|
||||
try {
|
||||
cache = caches.at(i);
|
||||
} catch (const std::out_of_range& oor) {
|
||||
LOG(LOG_ERROR) << FUNC_NAME << ": cache index out of range: " << oor.what();
|
||||
continue;
|
||||
}
|
||||
if ((asset = cache->loadAsset(filename)) != NULL) return asset;
|
||||
}
|
||||
LOG(LOG_INFO) << FUNC_NAME << " " << filename << " " << std::hex << crc32(1337, buffer, len);
|
||||
LOG(LOG_INFO) << filename << ": " << std::hex << crc32(1337, filename, strlen(filename)) << " " << std::hex << crc32(1337, buffer, len) << " " << std::hex << crc32(1337, buffer, len/2) << " " << std::hex << crc32(1337, buffer+(len/2), len/2);
|
||||
free(buffer);
|
||||
return NULL;
|
||||
// if we got to here, we failed to find an asset
|
||||
asset = &null_asset;
|
||||
return asset;
|
||||
}
|
||||
/* ======== Asset Cache ======== */
|
||||
int AssetManager::addCache(AssetCache *cache) {
|
||||
caches.push_back(cache);
|
||||
return 0;
|
||||
}
|
|
@ -33,10 +33,12 @@ class AssetManager {
|
|||
// Asset* loadAsset(const char *uuid);
|
||||
// saves the loaded assets in the asset cache
|
||||
// saves all Assets in assets to the given cache file
|
||||
int addCache(AssetCache* cache);
|
||||
// int saveCache(const char *cachefile);
|
||||
//
|
||||
// int loadCache(const char *cachefile)
|
||||
private:
|
||||
std::vector<AssetCache> caches; // Caches
|
||||
Asset null_asset; // Special null asset to return from loadAsset if it could not be found
|
||||
std::vector<AssetCache*> caches; // Caches
|
||||
AssetCache live_cache; // Cache of loaded assets
|
||||
};
|
||||
};
|
35
src/Core.cpp
35
src/Core.cpp
|
@ -20,7 +20,7 @@ Core::Core() {
|
|||
asset_manager = NULL;
|
||||
}
|
||||
Core::~Core() {
|
||||
|
||||
if (asset_manager != NULL) delete asset_manager;
|
||||
}
|
||||
int Core::initSystem() {
|
||||
if (SDL_Init(SDL_INIT_EVERYTHING & ~SDL_INIT_HAPTIC) != 0) {
|
||||
|
@ -80,6 +80,11 @@ int Core::initSystem() {
|
|||
// TEMP: this should be assigned elsewhere.
|
||||
asset_manager = new AssetManager();
|
||||
// TODO: load APP cache as first cache (using ApkAssetCache in the case of Android)
|
||||
#ifdef __ANDROID__
|
||||
asset_manager->addCache(new ApkAssetCache(""));
|
||||
#else
|
||||
asset_manager->addCache(new AssetCache("data"));
|
||||
#endif
|
||||
// TODO: load USER cache as second
|
||||
// TODO: add others via user pref?
|
||||
|
||||
|
@ -114,28 +119,36 @@ int Core::initSystem() {
|
|||
return 1;
|
||||
}
|
||||
#else
|
||||
if (program->addShader("data/shaders/fb_fs.glsl", GL_FRAGMENT_SHADER) != 0) {
|
||||
Asset *shader_asset = asset_manager->loadFile("data/shaders/fb_fs.glsl");
|
||||
if (program->addShader(shader_asset->getData(), shader_asset->getDataLength(), GL_FRAGMENT_SHADER) != 0) {
|
||||
LOG(LOG_ERROR) << "Failed to add Shader!";
|
||||
return 1;
|
||||
}
|
||||
if (program->addShader("data/shaders/fb_vs.glsl", GL_VERTEX_SHADER) != 0) {
|
||||
shader_asset->unloadData();
|
||||
shader_asset = asset_manager->loadFile("data/shaders/fb_vs.glsl");
|
||||
if (program->addShader(shader_asset->getData(), shader_asset->getDataLength(), GL_VERTEX_SHADER) != 0) {
|
||||
LOG(LOG_ERROR) << "Failed to add Shader!";
|
||||
return 1;
|
||||
}
|
||||
shader_asset->unloadData();
|
||||
if (program->doCompile() != 0) {
|
||||
LOG(LOG_ERROR) << "Failed to compile GLSL Program!";
|
||||
return 1;
|
||||
}
|
||||
// TEMP: our model rendering program
|
||||
Program *program_model = new Program();
|
||||
if (program_model->addShader("data/shaders/default_fs.glsl", GL_FRAGMENT_SHADER) != 0) {
|
||||
shader_asset = asset_manager->loadFile("data/shaders/default_fs.glsl");
|
||||
if (program_model->addShader(shader_asset->getData(), shader_asset->getDataLength(), GL_FRAGMENT_SHADER) != 0) {
|
||||
LOG(LOG_ERROR) << "Failed to add Shader!";
|
||||
return 1;
|
||||
}
|
||||
if (program_model->addShader("data/shaders/default_vs.glsl", GL_VERTEX_SHADER) != 0) {
|
||||
LOG(LOG_ERROR) << "Failed to add Shdaer!";
|
||||
shader_asset->unloadData();
|
||||
shader_asset = asset_manager->loadFile("data/shaders/default_vs.glsl");
|
||||
if (program_model->addShader(shader_asset->getData(), shader_asset->getDataLength(), GL_VERTEX_SHADER) != 0) {
|
||||
LOG(LOG_ERROR) << "Failed to add Shader!";
|
||||
return 1;
|
||||
}
|
||||
shader_asset->unloadData();
|
||||
if (program_model->doCompile() != 0) {
|
||||
LOG(LOG_ERROR) << "Failed to compile GLSL Program!";
|
||||
return 1;
|
||||
|
@ -157,16 +170,20 @@ int Core::initSystem() {
|
|||
RenderSet *sert = new RenderSet();
|
||||
sert->setProgram(program_model);
|
||||
|
||||
asset_manager->loadFile("data/models/cube.obj");
|
||||
Asset *asset_mesh;
|
||||
|
||||
RenderObject *objerct = new RenderObject();
|
||||
Mesh *mersh = new Mesh("data/models/cube.obj");
|
||||
asset_mesh = asset_manager->loadFile("data/models/cube.obj");
|
||||
Mesh *mersh = new Mesh(asset_mesh->getData(), asset_mesh->getDataLength());
|
||||
asset_mesh->unloadData();
|
||||
mersh->buildMesh();
|
||||
objerct->setMesh(mersh);
|
||||
sert->addObject(objerct);
|
||||
|
||||
objerct = new RenderObject();
|
||||
mersh = new Mesh("data/models/chest.obj");
|
||||
asset_mesh = asset_manager->loadFile("data/models/chest.obj");
|
||||
mersh = new Mesh(asset_mesh->getData(), asset_mesh->getDataLength());
|
||||
asset_mesh->unloadData();
|
||||
mersh->buildMesh();
|
||||
objerct->setMesh(mersh);
|
||||
objerct->setTranslation(-5.0f, 0.0f, 5.0f);
|
||||
|
|
|
@ -0,0 +1,233 @@
|
|||
/* ================================================================
|
||||
HashTable
|
||||
````````````````
|
||||
HashTable is a template class for storing the provided data type T along with an identifier key as a char*. Values are stored in the predefined HashTable with a hash of their key used as the position offset. Data collisions are handled by storing data in a HashEntry object and checking if the key itself does not match a stored copy. If the key does not match, the HashEntry provides a pointer to the next HashEntry. This continues on as a linked list until a match is or is not found.
|
||||
|
||||
If a match is not found, the HashTable returns a special user-defined "null" variable that should match the provided data type to be stored.
|
||||
|
||||
A HashTable may be resized, but it should _ONLY_ be resized if there is _NO_ data in the table. Future iterations of this template may allow for resizing by creating a new table, copying over the old stored data into their new positions, then replacing the old table with the new one.
|
||||
================================================================ */
|
||||
#ifndef HASHTABLE_HPP
|
||||
#define HASHTABLE_HPP
|
||||
#include <stdlib.h> // malloc
|
||||
#include <string.h> // strlen
|
||||
#include <limits.h> // INT_MAX
|
||||
#include <iostream>
|
||||
|
||||
template<class T> class HashEntry {
|
||||
public:
|
||||
HashEntry(const char *key_, T data_) {
|
||||
int key_len = strlen(key_)+1;
|
||||
key = (char*)malloc(key_len*sizeof(char));
|
||||
memcpy(key, key_, key_len*sizeof(char));
|
||||
data = data_;
|
||||
next = NULL;
|
||||
};
|
||||
~HashEntry() {
|
||||
free(key);
|
||||
};
|
||||
char *key;
|
||||
T data;
|
||||
HashEntry *next;
|
||||
};
|
||||
|
||||
template<class T> class HashTable {
|
||||
public:
|
||||
HashTable() { HashTable(128); };
|
||||
HashTable(int hash_size) {
|
||||
pos = 0; // iterator pos
|
||||
size = hash_size;
|
||||
iter = NULL;
|
||||
last_iter = NULL;
|
||||
table = (HashEntry<T>**)malloc(sizeof(HashEntry<T>*)*size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
table[i] = NULL;
|
||||
}
|
||||
};
|
||||
~HashTable() {
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (table[i] != NULL) delete table[i];
|
||||
}
|
||||
free(table);
|
||||
};
|
||||
// resize should _only_ be called when the HashTable has _no_ data!
|
||||
void resize(int hash_size) {
|
||||
size = hash_size;
|
||||
table = (HashEntry**)realloc(table, sizeof(HashEntry*)*size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
table[i] = NULL;
|
||||
}
|
||||
};
|
||||
void setnull(T null_value) {
|
||||
null = null_value;
|
||||
}
|
||||
//
|
||||
T get(const char* key) {
|
||||
int key_hash = getHash(key);
|
||||
// find our hash if it exists
|
||||
HashEntry<T> *entry = table[key_hash];
|
||||
while (entry != NULL) {
|
||||
if (strcmp(entry->key, key) == 0) {
|
||||
return entry->data;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
// doesn't exist, return the user's null value
|
||||
return null;
|
||||
};
|
||||
bool exists(const char *key) {
|
||||
int key_hash = getHash(key);
|
||||
// find our hash if it exists
|
||||
HashEntry<T> *entry = table[key_hash];
|
||||
while (entry != NULL) {
|
||||
if (strcmp(entry->key, key) == 0) {
|
||||
return true;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
// doesn't exist, return the user's null value
|
||||
return false;
|
||||
}
|
||||
T set(const char *key, T value) {
|
||||
int key_hash = getHash(key);
|
||||
// find our hash if it exists
|
||||
HashEntry<T> *entry = table[key_hash];
|
||||
if (entry == NULL) {
|
||||
table[key_hash] = new HashEntry<T>(key, value);
|
||||
return table[key_hash]->data;
|
||||
}
|
||||
while (entry->next != NULL) {
|
||||
if (strcmp(entry->key, key) == 0) {
|
||||
entry->data = value;
|
||||
return entry->data;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
entry->next = new HashEntry<T>(key, value);
|
||||
return entry->next->data;
|
||||
};
|
||||
void del(const char *key) {
|
||||
int key_hash = getHash(key);
|
||||
HashEntry<T> *entry = table[key_hash];
|
||||
HashEntry<T> *last_entry = NULL;
|
||||
if (entry != NULL) {
|
||||
if (strcmp(entry->key, key) == 0) {
|
||||
table[key_hash] = entry->next;
|
||||
delete entry;
|
||||
return;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
while (entry != NULL) {
|
||||
if (strcmp(entry->key, key) == 0) {
|
||||
if (last_entry != NULL) last_entry->next = entry->next;
|
||||
delete entry;
|
||||
return;
|
||||
}
|
||||
last_entry = entry;
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
int getHash(const char *key) {
|
||||
int key_hash = 0;
|
||||
int key_len = strlen(key);
|
||||
// generate our hash
|
||||
for (int i = 0; key_hash < INT_MAX && i < key_len; i++) {
|
||||
key_hash += key[i];
|
||||
}
|
||||
// limit hash to the HashTable's size and return
|
||||
return (key_hash % size);
|
||||
}
|
||||
/*T iterate() {
|
||||
int i = 1;
|
||||
while(i) {
|
||||
if (iter == NULL) {
|
||||
if (pos > size) {
|
||||
iter = NULL;
|
||||
pos = 0;
|
||||
return null;
|
||||
}
|
||||
iter = table[pos++];
|
||||
} else {
|
||||
if (iter->data != null) {
|
||||
std::cout << "found one at" << i-1 << std::endl;
|
||||
HashEntry *ret = iter;
|
||||
iter = iter->next;
|
||||
return ret->data;
|
||||
}
|
||||
}
|
||||
}
|
||||
return iter->data;
|
||||
}*/
|
||||
template <class T> T operator[](char* key) {
|
||||
int key_hash = 0;
|
||||
int key_len = strlen(key);
|
||||
// get our key hash
|
||||
for (int i = 0; key_hash < INT_MAX && i < key_len; i++) {
|
||||
key_hash += key[i];
|
||||
}
|
||||
// limit it to our HashTable's size
|
||||
key_hash = key_hash % size;
|
||||
// find our hash if it exists
|
||||
HashEntry *entry = table[key_hash];
|
||||
while (entry != NULL) {
|
||||
if (strcmp(entry->key, key) == 0) {
|
||||
return entry->data;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
// doesn't exist, return the user's null value
|
||||
return null;
|
||||
}
|
||||
template <class T> T& operator[](char *key) const {
|
||||
int key_hash = 0;
|
||||
int key_len = strlen(key);
|
||||
// get our key hash
|
||||
for (int i = 0; key_hash < INT_MAX && i < key_len; i++) {
|
||||
key_hash += key[i];
|
||||
}
|
||||
// limit it to our HashTable's size
|
||||
key_hash = key_hash % size;
|
||||
// find our hash if it exists
|
||||
HashEntry *last_entry, *entry = table[key_hash];
|
||||
if (entry == NULL) {
|
||||
table[key_hash] = new HashEntry(key, null);
|
||||
return entry->data;
|
||||
}
|
||||
while (entry->next != NULL) {
|
||||
if (strcmp(entry->key, key) == 0) {
|
||||
return entry->data;
|
||||
}
|
||||
last_entry = entry;
|
||||
entry = entry->next;
|
||||
}
|
||||
entry->next = new HashEntry(key, null);
|
||||
return entry->next;
|
||||
}
|
||||
HashEntry<T> *iterate() {
|
||||
int i = 1;
|
||||
while(i) {
|
||||
if (iter == NULL) {
|
||||
if (pos >= size) {
|
||||
iter = NULL;
|
||||
pos = 0;
|
||||
return NULL;
|
||||
}
|
||||
iter = table[pos++];
|
||||
} else {
|
||||
HashEntry<T> *ret = iter;
|
||||
iter = iter->next;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
private:
|
||||
int size;
|
||||
T null; // user defined "null" value
|
||||
HashEntry<T> **table;
|
||||
int pos;
|
||||
HashEntry<T> *iter;
|
||||
HashEntry<T> *last_iter;
|
||||
};
|
||||
#endif
|
52
src/Mesh.cpp
52
src/Mesh.cpp
|
@ -10,11 +10,11 @@ Mesh::Mesh() {
|
|||
flags = 0;
|
||||
mode = GL_STATIC_DRAW;
|
||||
}
|
||||
Mesh::Mesh(const char *filename) {
|
||||
Mesh::Mesh(const char *buffer, size_t buffer_size) {
|
||||
nbo = uvbo = vbo = vao = tex = 0;
|
||||
flags = 0;
|
||||
mode = GL_STATIC_DRAW;
|
||||
loadObj(filename);
|
||||
loadObj(buffer, buffer_size);
|
||||
}
|
||||
Mesh::Mesh(Vec3 *in_vertices, int v_count, Vec2 *in_uvs, int uv_count, Vec3 *in_normals, int n_count) {
|
||||
nbo = uvbo = vbo = vao = tex = 0;
|
||||
|
@ -27,32 +27,38 @@ Mesh::~Mesh() {
|
|||
if (flags & MESH_BUILT) destroyMesh();
|
||||
}
|
||||
|
||||
int Mesh::loadObj(const char *filename) {
|
||||
int Mesh::loadObj(const char *buffer, size_t buffer_size) {
|
||||
std::vector <Vec3> temp_vertices;
|
||||
std::vector <Vec2> temp_uvs;
|
||||
std::vector <Vec3> temp_normals;
|
||||
std::vector <unsigned int> vertex_indices;
|
||||
std::vector <unsigned int> uv_indices;
|
||||
std::vector <unsigned int> normal_indices;
|
||||
int ret;
|
||||
FILE *file = asset_fopen(filename, "r");
|
||||
if (file == NULL) {
|
||||
LOG(LOG_ERROR) << "Could not open model: " << filename;
|
||||
if (buffer == NULL || buffer_size == 0) {
|
||||
LOG(LOG_WARNING) << FUNC_NAME << ": passed buffer is empty";
|
||||
return 1;
|
||||
}
|
||||
char word[32];
|
||||
char word[128];
|
||||
char *cpos = (char*)buffer;
|
||||
size_t offset = 0;
|
||||
size_t bpos = 0;
|
||||
#ifdef _WIN32
|
||||
while ((ret = fscanf_s(file, "%s ", word)) != EOF) {
|
||||
while (sscanf_s(cpos, "%s %n", word, 128, &offset) == 1) {
|
||||
#else
|
||||
while ((ret = fscanf(file, "%s ", word)) != EOF) {
|
||||
while (sscanf(cpos, "%s %n", word, &offset) == 1) {
|
||||
#endif
|
||||
bpos += offset;
|
||||
cpos += offset;
|
||||
if (strcmp(word, "v") == 0) { // vertices
|
||||
// "x y z w" as floats, with "w" being optional
|
||||
Vec3 vec; // our vertex
|
||||
char word[32];
|
||||
char word[128];
|
||||
char ch;
|
||||
int pos = 0, w_i = 0;
|
||||
while ((ch = fgetc(file)) != EOF) {
|
||||
while (bpos < buffer_size) {
|
||||
ch = *cpos;
|
||||
bpos++;
|
||||
cpos++;
|
||||
if (ch == ' ' || ch == '\n') {
|
||||
if (pos == 0) { // x
|
||||
word[w_i] = '\0';
|
||||
|
@ -80,7 +86,10 @@ int Mesh::loadObj(const char *filename) {
|
|||
char word[32];
|
||||
char ch;
|
||||
int pos = 0, w_i = 0;
|
||||
while ((ch = fgetc(file)) != EOF) {
|
||||
while (bpos < buffer_size) {
|
||||
ch = *cpos;
|
||||
bpos++;
|
||||
cpos++;
|
||||
if (ch == ' ' || ch == '\n') {
|
||||
if (pos == 0) { // x
|
||||
word[w_i] = '\0';
|
||||
|
@ -105,7 +114,10 @@ int Mesh::loadObj(const char *filename) {
|
|||
char word[32];
|
||||
char ch;
|
||||
int pos = 0, w_i = 0;
|
||||
while ((ch = fgetc(file)) != EOF) {
|
||||
while (bpos < buffer_size) {
|
||||
ch = *cpos;
|
||||
bpos++;
|
||||
cpos++;
|
||||
if (ch == ' ' || ch == '\n') {
|
||||
if (pos == 0) { // x
|
||||
word[w_i] = '\0';
|
||||
|
@ -138,7 +150,10 @@ int Mesh::loadObj(const char *filename) {
|
|||
char ch;
|
||||
int w_i = 0, mode = 0;
|
||||
// read in our faces into our index vectors
|
||||
while ((ch = fgetc(file)) != EOF) {
|
||||
while (bpos < buffer_size) {
|
||||
ch = *cpos;
|
||||
bpos++;
|
||||
cpos++;
|
||||
if (ch == ' ' || ch == '\n') {
|
||||
word[w_i] = '\0';
|
||||
if (mode == 0) { // was in vertex, now in texture-coords
|
||||
|
@ -171,12 +186,14 @@ int Mesh::loadObj(const char *filename) {
|
|||
// "s 1" or "s off"
|
||||
} else { // ergo, newline noms
|
||||
char ch;
|
||||
while ((ch = fgetc(file)) != EOF) {
|
||||
while (bpos < buffer_size) {
|
||||
ch = *cpos;
|
||||
bpos++;
|
||||
cpos++;
|
||||
if (ch == '\n') break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
// we're done, let's convert our face-derived indices to our actual mesh
|
||||
int i;
|
||||
int indices = vertex_indices.size();
|
||||
|
@ -200,6 +217,7 @@ int Mesh::loadObj(const char *filename) {
|
|||
flags |= MESH_LOADED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Mesh::loadArrays(Vec3 *in_vertices, int v_count, Vec2 *in_uvs, int uv_count, Vec3 *in_normals, int n_count) {
|
||||
if (v_count > 0) {
|
||||
vertices.assign(in_vertices, in_vertices + v_count);
|
||||
|
|
|
@ -15,11 +15,11 @@ A Mesh is as the name indicates, a 3D mesh.
|
|||
class Mesh {
|
||||
public:
|
||||
Mesh();
|
||||
Mesh(const char *filename);
|
||||
Mesh(const char *buffer, size_t buffer_size);
|
||||
Mesh(Vec3 *in_vertices, int v_count, Vec2 *in_uvs, int uv_count, Vec3 *in_normals, int n_count);
|
||||
~Mesh();
|
||||
//
|
||||
int loadObj(const char *filename);
|
||||
int loadObj(const char *buffer, size_t buffer_size);
|
||||
int loadArrays(Vec3 *in_vertices, int v_count, Vec2 *in_uvs, int uv_count, Vec3 *in_normals, int n_count);
|
||||
int buildMesh();
|
||||
int destroyMesh();
|
||||
|
|
|
@ -19,28 +19,23 @@ Program::~Program() {
|
|||
}
|
||||
/* ======== Compilation and Linking ======== */
|
||||
// add given shader to Program. During doCompile(), it is compiled and attached to the program
|
||||
int Program::addShader(const char *file, GLenum type) {
|
||||
// Load in our shader file
|
||||
char *buffer = NULL;
|
||||
size_t length = asset_fileToMem(file, &buffer);
|
||||
buffer[length] = '\0';
|
||||
int Program::addShader(const char *buffer, size_t length, GLenum type) {
|
||||
if (buffer == NULL) {
|
||||
LOG(LOG_ERROR) << FUNC_NAME << ": Could not open \"" << file << "\" for reading";
|
||||
LOG(LOG_ERROR) << FUNC_NAME << ": passed buffer was empty!";
|
||||
return 1;
|
||||
}
|
||||
// Create our shader
|
||||
GLuint shader = glCreateShader(type);
|
||||
if (shader == 0) {
|
||||
LOG(LOG_ERROR) << FUNC_NAME << ": Could not create shader for " << file;
|
||||
LOG(LOG_ERROR) << FUNC_NAME << ": Could not create shader!";
|
||||
return 2;
|
||||
}
|
||||
// Load GLSL program into OpenGL
|
||||
const char *c_str = buffer;
|
||||
glShaderSource(shader, 1, &c_str, NULL);
|
||||
glShaderSource(shader, 1, &c_str, (const int*)&length);
|
||||
// Push the shader and its type onto the appropriate vectors
|
||||
shader_prog.push_back(shader);
|
||||
shader_type.push_back(type);
|
||||
free(buffer);
|
||||
return 0;
|
||||
}
|
||||
int Program::doCompile() {
|
||||
|
|
|
@ -38,7 +38,7 @@ class Program {
|
|||
public:
|
||||
Program();
|
||||
~Program();
|
||||
int addShader(const char *file, GLenum type);
|
||||
int addShader(const char *buffer, size_t length, GLenum type);
|
||||
int attachTexture(GLuint index, GLuint texture);
|
||||
int setTexture(GLuint index, GLuint texture);
|
||||
int doCompile();
|
||||
|
|
12
src/fio.cpp
12
src/fio.cpp
|
@ -15,7 +15,7 @@ If any file is attempted to be read or written to, fio.hpp should be included fi
|
|||
*/
|
||||
size_t asset_fileToMem(const char *filename, char **buffer) {
|
||||
char chunk[1024];
|
||||
FILE *file = asset_fopen(filename, "r");
|
||||
FILE *file = asset_fopen(filename, "rb");
|
||||
if (file == NULL) {
|
||||
LOG(LOG_ERROR) << FUNC_NAME << ": could not open " << filename;
|
||||
return 0;
|
||||
|
@ -25,6 +25,10 @@ size_t asset_fileToMem(const char *filename, char **buffer) {
|
|||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
*buffer = (char*)malloc(size*sizeof(char));
|
||||
if (*buffer == NULL) {
|
||||
LOG(LOG_ERROR) << FUNC_NAME << ": could not allocate " << size << " bytes for asset " << filename;
|
||||
return NULL;
|
||||
}
|
||||
int n = 0;
|
||||
size_t offset = 0;
|
||||
while ((n = fread(chunk, 1, 1024, file))) {
|
||||
|
@ -44,9 +48,11 @@ FILE *asset_fopen(const char *filename, const char *mode) {
|
|||
#ifdef __ANDROID__
|
||||
return apk_fopen(filename, mode);
|
||||
#elif _WIN32
|
||||
FILE *file;
|
||||
/*FILE *file;
|
||||
fopen_s(&file, filename, mode);
|
||||
return file;
|
||||
return file;*/
|
||||
// _fsopen is used as we pass a FILE ptr back - fopen_s does not allow this
|
||||
return _fsopen(filename, mode, _SH_DENYNO);
|
||||
#else
|
||||
return fopen(filename, mode);
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue