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
kts 2015-03-01 02:17:12 -08:00
parent 05a43ddcdb
commit bc831f05b0
16 changed files with 581 additions and 64 deletions

View File

@ -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" />

View File

@ -157,5 +157,8 @@
<ClInclude Include="..\..\src\AssetCache.hpp">
<Filter>Classes</Filter>
</ClInclude>
<ClInclude Include="..\..\src\HashTable.hpp">
<Filter>Classes</Filter>
</ClInclude>
</ItemGroup>
</Project>

10
data/.CACHE 100644
View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View 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__

View File

@ -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;
}

View File

@ -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
};
};

View File

@ -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);

233
src/HashTable.hpp 100644
View File

@ -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

View File

@ -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);

View File

@ -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();

View File

@ -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() {

View File

@ -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();

View File

@ -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