Jump to content

  • Log In with Google      Sign In   
  • Create Account

Vincent_M

Member Since 16 Jan 2007
Offline Last Active Feb 09 2016 10:09 PM

#5247304 Different Resolutions

Posted by Vincent_M on 17 August 2015 - 10:06 PM

As mentioned above, there are many ways to handle this. One way we dealt with this at a video game company I worked at was work in a fixed-height coordinate space. Instead of having all of your coordinates match up to pixels, come up with some arbitrary fixed height value (test it out though). The fixed height will be the domain of your screen's coordinate space. The width would vary between screen sizes, and you could find it by multiplying your fixed height value by the aspect ratio. That said, make sure all of your fullscreen background artwork produced with the height of your fixed-height value, and its width being whatever it is multiplied by your most extreme aspect ratio (most-likely 16:9, if working in landscape). Then, the sides would just be clipped on screens with fuller aspect ratios, most-notably the 4:3 aspect ratio. You'd design most of your UI in the middle of the screen, making sure it fits nicely in 4:3. You could attach certain UI elements to the edges of the screen so that they hug the sides regardless of fullscreen or widescreen aspect ratios. 9-slicing will be a huge part of this as well. A good deal of your panels and other large UI elements would most-likely be percentage-based if you want them to scale with the screen. The beauty here is that your UI is no longer resolution-dependent, and mostly not aspect-ratio dependent. You could get more creative, and have different resolution images for different resolution devices. For example, high-res iamges for Quad HD and 4K displays (many mobile devices are going Quad HD today). You could use lower-resolution versions of your images for lower-resolution devices --something I highly recommend. Lower-resolution devices are generally older devices as resolutions have been increasing almost every year. These older devices won't have the memory, or the GPU bandwidth to support these higher-resolution textures, so I'd suggest having some sort of content pipeline in place that automatically cooks up low-end builds with lower-resolution images.

 

Regarding having multiple resolutions of your game's assets, I'd use PC games as an example. With varying hardware and screen resolutions, gamers can usually lower graphical settings, such as texture resolutions, if there isn't enough resources to go around.

 

You can also use techniques mentioned above alongside this process.




#5244311 Constructor Initializer List and Heap Allocation

Posted by Vincent_M on 03 August 2015 - 10:24 AM

Whenever I have a field that should be initialized onto the heap upon initialization in a class, I usually initialize it to nullptr in the initializer list, and then allocate it in the constructor's body. For example:

class Test
{
private:
Model *model;

public:

Test() : model(nullptr)
{
model = new Model();
}

~Test()
{
if(model) delete model;
}

};

I notice in Qt, that my designer-derived class will allocate its UI pointer onto the heap in the constructor's initializer list. Is this a good practice? If my heap-allocated objects' constructors are lean, would this make better sense?




#5243902 [Qt 5] Moving Rows For Scene Tree (QAbstractItemModel)

Posted by Vincent_M on 31 July 2015 - 03:48 PM


I'm having some problems trying to read the post. What are you trying to ask exactly?

I'm having issues with dropMimeData() in my code whenever have it return true on a successful drop. Nodes seem to disappear a frame later. I found out why, and I'll elaborate below.

 

 

 


I don't have much experience in the area, when I have to deal with QAbstractItemModel it's usually for QML and everything I do there works (which is not much and less than you want there). Have you tried doing something that absolutely forces the QTreeView to reevaluate the model? Like setting an empty model and then setting the real model back? That's not a solution but it would be nice to know for sure if the model is basically sound.

I wanted to do this myself, but I'm unable to. The reason being because I need to reset my model once SceneModel's dropMimeData() method is called. I would emit a signal to a slot in my MainWindow class where I could reset my model. The problem is, it seems like the only way one could reset a model in Qt5 is by unsetting the model from the QTreeView, releasing the model from memory, reallocating it, and re-setting the newly allocated model to the QTreeView. This will cause a crash because this is happening from emitting a signal within the scope of a method from an object that's going to be destroyed during the emitting call because the method hasn't returned yet.

 

 

Solution Found!

I found that when SceneModel::dropMimeData() returns true, SceneModel's base class, QAbstractItemModel, would internally call removeRows() on the node I just moved. My reimplementation of removeRows(), which is required, tells the parent node to delete the new child node I just moved under it. This would make sense why my pointers to nodes became dangling pointers. I looked into why this was, and it was due to my dropAction being set to InternalMove for my QTreeView. I thought InternalMove was what I needed, but it looks like DragDrop, does the job. The dangling pointer issue is resolved since my SceneModel no-longer removes nodes unexpectedly. My QTreeView's drag and drop performance also seems to work more fluidly. I used to have to select my node in a sweet spot to get it to work. I can also drag nodes into my the top-level hierarchy.

 

My hierarchy system is usable, but still kind of clunky. Here is a list of outstanding issues:

  1. My QTreeView only has single selection enabled, but when I child two nodes under another node, the second node under the new parent is always selected until I select it.
  2. Whenever I child a node, selection defaults to the second child to the parent node instead of what was selected
  3. Whenever moving a node to another node, it always gets added to the bottom of the list when I place it between two nodes. I have to set its parent, then I can reorder the siblings. I think this is due to my Node class' functionality.
  4. Whenever dragging Nodes to the top-level view, I'll get a warning in the console saying that there's an invalid index, although I still get the desired results.

 

I've found some answers to these issues:

  1. It appears that whatever sourceRow is highlights that row indefinitely for some reason. It happened to be the second row under the new parent because I've been dragging the second child to the scene's root (index of 1) to the new parent. Then, I provide sourceRow to beginMoveRows(). I think this is to preserve selection after a move occurs, but it should be based on the destination child's index. After some Googling, I found that another person was having an issue with how their QTreeView would visually display his model's selection. It was due to how he overrode QAbstractItemModel::parent(). My thought was if modifying parent() fixed his selection behavior, then that's probably where my issue lies. In my parent() implementation, I wasn't allowing parent() to create a valid QModelIndex if the Node's parent was the scene's root. This is advice I got from a previous tutorial, but I'm not sure why that logic's there. I'm not sure what parent() even does. I removed the extra check against my scene's root, and this issue seems to be resolved.
  2. See the previous point. This is fixed.
  3. This was my SetParent() logic, as mentioned previously. I've added additional logic to my SetParent() method that resolves this issue.
  4. Still working on this, but I think it has to do with how I'm passing parameters to beginMoveRows().



#5243661 2D Platformer Camera

Posted by Vincent_M on 30 July 2015 - 03:55 PM

Does your 2D camera or any of the tiles rotate? If not, your camera and all tiles are axis-aligned bounding boxes (AABBs). For a simple culling test, you could just loop through every tile in your level, and do a simple AABB intersection test. Looping through each tile in your scene can also be a bottleneck if you have a lot of tiles. A better solution would be to use a scene graph at that point. You'd pre-calculate them into a quadtree, or whatever scene graph you'd like. Then, loop through the scene graph's nodes recursively, and add all bottom-level nodes' tiles to the batch buffer for that frame.




#5243654 Safety vs Efficiency

Posted by Vincent_M on 30 July 2015 - 03:33 PM

Using asserts for code that shouldn't go wrong. For example: pointers that can't ever be NULL/invalid because they're allocated in the constructor. Code changes, and guarantees that exist today probably won't tomorrow. It's common that you'll have enough on your plate that you'll occasionally overlook some piece of code that relies on conditions no longer guarantee this code. Assert's good for these cases.




#5238636 Animated Model Format for ASSIMP

Posted by Vincent_M on 06 July 2015 - 10:59 AM

I'm currently using ASSIMP as a decent model importer just to get something up and running. Upon deployment, any model data I'll use will get serialized into an engine-ready format to be actually used in a production environment. It supports a variety formats that are decent for static meshes, such as .3ds, .ase, .lwo and .obj. The problem is, I'm not sure about which format to export skinned, animated models to. MD5 might work, but I've yet to find a decent exporter that'll work in Max, Maya and Blender. I've never written an importer and renderer for MD5 in the past. They have COLLADA support, but that format's MASSIVE in both spec and file size as it's in XML rather than JSON for a text-based schema. I've never seen a decent exporter for COLLADA either. ASSIMP doesn't list FBX as a supported model format, but I've seen that it has limited support for that in the past as you have to export with an old-enough version of the FBX spec. It's also a massively-complex 3D model format (like COLLADA) that's designed to cover anything from architectural data for CAD to real-time game models. Autodesk also appears to update its format every 6 months, the libraries are way more complex than needed for me, the licensing is restrictive (I think, anyway), and new ones are released every 6 months by Autodesk. There's support for .blend files, but those are binary scene dumps created by Blender, and not really meant to be used as an intermediate model format. That said, the format's like FBX: overly-complex for a game's needs, and the data format may change from version to version of Blender, which gets updated every 6 months. Any suggestions here?

 

Then, there's OpenGEX for my intermediate, animated model solution:

The OpenGEX format was developed, along with the markup language it's built on top of, OpenDDL (data description language/layer). It was developed by Eric Lengyel, author of the C4 Engine (he's pretty frequent on GameDev.Net), as the result of a successful crowd funding campaign back in 2013. The format seems pretty solid for a jack-of-all-trades format. It's another attempt at what COLLADA and FBX have tried to do in the past as being a generic 3D model standard. It's an open, well-documented format, unlike FBX, and has a MUCH simpler spec than both FBX and COLLADA. The text overhead due to metadata is also kept at a minimum. He's also released exporters for all 3 major 3D modeling packages, and provides some good boilerplate code to get started in writing an importer as well. I have nothing to say, but good things about this format's structure.

 

That said, there are some drawbacks:

1) Although really nice and generic to the point where it's simple, it's still pretty complex for my needs for what I need to do right now. I went through the importer template back in November, and I did come to the conclusion that I could write an implementation for a basic model importer. I'm not sure how well I'd do to support animations just yet, but the main problem is me. There's SO much potential here, and I'm like a kid in a candy shop: I want it all. I want to support everything, and I can't seem to keep to just the current project requirements. I'm also concerned that the format will undergo change over time that could break previous functionality. Not sure how valid that concern it though.

2) The exporters seem pretty solid, and maintained by Eric, but if I recall, there are some slight differences in support for the exporters between 3D modeling packages. I remember the Blender version not supporting LODs for meshes, which is probably more of a Blender thing than an OpenGEX thing. Not really necessary for me, but there might be other support Blender lacks as that's probably what we'll use.

3) Not a huge adoption yet. Although it's pretty solid, and of course, part of Eric's own C4 Engine, I haven't seen others use it much. There's a lot of official documentation on it, but it'd be nice to talk to others about their experience implementing it. I personally hope it takes off.

 

The more I talk about OpenGEX, the more I want to pick up where I left off with it. I already have a pretty solid OBJ loader for static models. It seems to load any OBJ I've found online or made in Blender so far. It's also been able to optimize it well enough as well. The OBJ importer also dumps its data into a generic Model class where attributes, and surface descriptions are held. It's a simple-enough mesh storage class that'd act as a nice in-memory application of OpenGEX's format nicely.

 

It's just that ASSIMP has all of these nice import options, such as normal generation, optimizing, etc. Then again, I have those features implemented...




#5230140 Signed-Distance Field Font

Posted by Vincent_M on 20 May 2015 - 05:14 PM

Can you post a picture of the signed distance field for the image?  That' might help.  Also, how big is the source image and how small is the scaled SDF alpha channel?

So this is where I think I'm doing it all wrong. I'm using Freetype2 to generate my glyph's source image. For the letter 'A', Freetype2 gives me a 18x18 pixel image. Then, I run through that image with the code above that'll add padding for the spread, then pad that image to a power of 2 size. I just save it out as an 8-bit buffer to a file, so it might be more convenient to provide you the ASCII version:

   0    0    0    0    0    0    0    0   25   25   25   25   25   25   25   25   25    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0 
   0    0    0    0    0    0    0   25   50   50   50   50   50   50   50   50   50   25    0    0    0    0    0    0    0    0    0    0    0    0    0    0 
   0    0    0    0    0    0   25   50   76   76   76   76   76   76   76   76   76   50   25    0    0    0    0    0    0    0    0    0    0    0    0    0 
   0    0    0    0    0    0   25   50   76  101  101  101  101  101  101  101   76   50   25    0    0    0    0    0    0    0    0    0    0    0    0    0 
   0    0    0    0    0    0   25   50   76  101  128  128  128  128  128  101   76   50   25    0    0    0    0    0    0    0    0    0    0    0    0    0 
   0    0    0    0    0   25   50   76   76  101  128  255  255  255  128  101   76   76   50   25    0    0    0    0    0    0    0    0    0    0    0    0 
   0    0    0    0    0   25   50   76  101  101  128  255  255  255  128  101  101   76   50   25    0    0    0    0    0    0    0    0    0    0    0    0 
   0    0    0    0   25   50   76   76  101  128  255  255  128  255  255  128  101   76   76   50   25    0    0    0    0    0    0    0    0    0    0    0 
   0    0    0    0   25   50   76  101  101  128  255  128  101  128  255  128  101  101   76   50   25    0    0    0    0    0    0    0    0    0    0    0 
   0    0    0    0   25   50   76  101  128  255  255  128  101  128  255  255  128  101   76   50   25    0    0    0    0    0    0    0    0    0    0    0 
   0    0    0   25   50   76   76  101  128  255  128  101  101  101  128  255  128  101   76   76   50   25    0    0    0    0    0    0    0    0    0    0 
   0    0    0   25   50   76  101  101  128  255  128  101   76  101  128  255  128  101  101   76   50   25    0    0    0    0    0    0    0    0    0    0 
   0    0    0   25   50   76  101  128  255  255  128  101   76  101  128  255  255  128  101   76   50   25    0    0    0    0    0    0    0    0    0    0 
   0    0   25   50   76   76  101  128  255  128  101  101  101  101  101  128  255  128  101   76   76   50   25    0    0    0    0    0    0    0    0    0 
   0    0   25   50   76  101  101  128  255  255  128  128  128  128  128  255  255  128  101  101   76   50   25    0    0    0    0    0    0    0    0    0 
   0   25   50   76   76  101  128  255  255  128  128  128  128  128  128  128  255  255  128  101   76   76   50   25    0    0    0    0    0    0    0    0 
   0   25   50   76  101  101  128  255  128  101  101  101  101  101  101  101  128  255  128  101  101   76   50   25    0    0    0    0    0    0    0    0 
   0   25   50   76  101  128  255  255  128  101   76   76   76   76   76  101  128  255  255  128  101   76   50   25    0    0    0    0    0    0    0    0 
  25   50   76   76  101  128  255  128  101  101   76   50   50   50   76  101  101  128  255  128  101   76   76   50   25    0    0    0    0    0    0    0 
  25   50   76  101  101  128  255  128  101   76   76   50   25   50   76   76  101  128  255  128  101  101   76   50   25    0    0    0    0    0    0    0 
  25   50   76  101  128  128  128  128  101   76   50   25    0   25   50   76  101  128  128  128  128  101   76   50   25    0    0    0    0    0    0    0 
  25   50   76  101  101  101  101  101  101   76   50   25    0   25   50   76  101  101  101  101  101  101   76   50   25    0    0    0    0    0    0    0 
  25   50   76   76   76   76   76   76   76   76   50   25    0   25   50   76   76   76   76   76   76   76   76   50   25    0    0    0    0    0    0    0 
   0   25   50   50   50   50   50   50   50   50   25    0    0    0   25   50   50   50   50   50   50   50   50   25    0    0    0    0    0    0    0    0 
   0    0   25   25   25   25   25   25   25   25    0    0    0    0    0   25   25   25   25   25   25   25   25    0    0    0    0    0    0    0    0    0 



#5194213 Textured Quads Appear White

Posted by Vincent_M on 22 November 2014 - 08:33 PM

I solved it. Turns out, the code above was fine, and the issue was within my vertex buffer:

glGenBuffers(2, vbo);
	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[1]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * numVertices, vertices, GL_STATIC_DRAW);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint16_t) * numIndices, indices, GL_STATIC_DRAW);
	glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)sizeof(Vector2));
	glEnableVertexAttribArray(0);
	glEnableVertexAttribArray(1);
	delete vertices;
	delete indices;

My second vertex attribute was set to 4 for the size, GL_UNSIGNED_BYTE for the type and GL_TRUE for the normalized arguments. This was a simple copy and paste mistake as the vertex format from another test state uses position and color. This one uses position and texture coordinates. False alarm!




#5191550 Resource management

Posted by Vincent_M on 06 November 2014 - 12:22 PM

I've thought against having a resource manager due to problems that could occur myself, but then my controller class ends up having a bunch of pointers to allocated resources for things like textures, models, scenes, etc. Then, having to explicitly release all of that data in my State's LoadContent() and UnloadContent() override methods gets kind of large for switching states and scenes. Then, I found myself writing the same resource loading code for most things, and that a subclass could handle a lot of the base functionality.

 

Since I'm using C++, I thought that OOP principles could help cut down on the redundant code. I think maybe my AssetManager system is taking on too much, honestly. I plan on having a File I/O namespace that'll include I/O classes for reading and writing data in binary blobs with speed and integrity checks as well as handling text/schema-based syntax formats such as XML and JSON for intermediate file data.

 

I really would like to learn more about software engineering, and systems development. I have quite a bit of experience doing things on my own. When it comes to developing a software system, such as an engine, or even a complete game, everything I'm currently capable of is a result of almost purely figuring things out on my own and experimentation. Are there any good software design books that cover OOP or important software engineering concepts in general, such as SRP? I've been reading papers online, but I'd like to find a course book that walks me through design patterns.




#5191384 Resource management

Posted by Vincent_M on 05 November 2014 - 01:12 PM

I've seen many arguments against a resource manager on these forums in the past in favor for writing a separate factory system per type of asset, but I think have a unified foundation with a factory system built on top for the specific needs of the asset is a better way to work on it. Here's the source to the beginning of my asset system:

#include <iostream>
#include <vector>
#include <map>


using namespace std;


class Asset
{
	template<class T> friend class AssetRef;
	
protected:
	bool persistent;
	vector<void**> references;
	
	virtual void Load() {}
	
public:
	Asset()
	{
		persistent = false;
	}
	
	~Asset()
	{
		for(int i=0;i<(int)references.size();++i)
			*references[i] = NULL;
		references.clear();
	}
	
	void MakePersistent(bool persistent) { this->persistent = persistent; }
	
	bool IsPersistent() { return persistent; }
	int GetReferenceCount() { return (int)references.size(); }
};


enum class AssetError
{
	Null=0,
	Exists,
	NotFound,
	DuplicateName,
	DuplicateAsset
};


template<class T>
class AssetRef
{
private:
	T *ref;
	
	void RemoveRef()
	{
		if(ref)
		{
			for(vector<void**>::iterator iter = ref->references.begin();iter != ref->references.end();++iter)
			{
				if(**iter == ref)
				{
					Asset *ptr = ref;
					ptr->references.erase(iter);
					break;
				}
			}
			ref = NULL;
		}
	}
	
public:
	AssetRef()
	{
		ref = NULL;
	}
	
	~AssetRef()
	{
		RemoveRef();
	}
	
	void operator=(T *opt2)
	{
		// reduce old reference's count, and set the new one if not NULL
		RemoveRef();
		if(opt2)
		{
			ref = opt2;
			ref->references.push_back((void**)&ref);
		}
	}
	
	T *Get() { return ref; }
};


class AssetManager
{
private:
	static map<string, Asset*> assets;
	
public:
	static bool Add(string name, Asset *asset)
	{
		// make sure the asset isn't NULL and the name is unique
		if(!asset || assets.find(name) != assets.end())
			return false;
		
		// make sure asset doesn't already exist
		map<string, Asset*>::iterator iter = assets.begin();
		while(iter != assets.end())
		{
			if(iter->second == asset)
			{
				cout << "asset already exists" << endl;
				return false;
			}
			++iter;
		}
		
		assets[name] = asset;
		return true;
	}
	
	static bool Remove(string name)
	{
		if(assets.find(name) == assets.end())
		{
			cout << "could not find asset: " << name << endl;
			return false;
		}
		
		//Base::Release(&assets[name]); // TODO: implement this
		assets.erase(name);
		return true;
	}
	
	template<class T>
	static void Purge()
	{
		map<string, Asset*> *purgeAssets = GetAssets<T*>();
		if(purgeAssets)
		{
			map<string, Asset*>::iterator iter = purgeAssets->begin();
			while(iter != purgeAssets->end())
			{
				Remove(iter->first);
				++iter;
			}
			delete purgeAssets;
		}
	}
	
	static void Flush()
	{
		// only flushes assets deemed non-persistent
	}
	
	static void Clear()
	{
		map<string, Asset*>::iterator iter = assets.begin();
		while(iter != assets.begin())
		{
			//Base::Release(&iter->second); // TODO: implement this
			iter->second = NULL;
			++iter;
		}
		assets.clear();
	}
	
	template<class T>
	static T *Get(string name)
	{
		// make sure an asset with that name exists
		if(assets.find(name) == assets.end())
		{
			cout << "could not find asset: " << name << endl;
			return NULL;
		}
		return dynamic_cast<T*>(assets[name]);
	}
	
	template<class T>
	static map<string, T*> GetAssets()
	{
		// allocate a new map and setup an iterator
		map<string, T*> *set = new map<string, T*>;
		map<string, Asset*>::iterator iter = assets.begin();
		
		// populate with the correct type of assets
		while(iter != assets.end())
		{
			T *ptr = dynamic_cast<T*>(iter->second);
			if(ptr)
				*set[iter->first] = iter->second;
			++iter;
		}
		
		// delete the allocate map if there are no assets of that type
		if(!set->size())
		{
			delete set;
			set = NULL;
		}
		return set;
	}
	
	template<class T>
	static int GetNumAssets()
	{
		int count = 0;
		map<string, Asset*>::iterator iter = assets.begin();
		while(iter != assets.end())
		{
			if(dynamic_cast<T*>(iter->second))
				++count;
			++iter;
		}
		return count;
	}
	
	static int GetNumAssets() { return (int)assets.size(); }
};


map<string, Asset*> AssetManager::assets;


class Texture2D : public Asset
{
};


class Font : public Asset
{
};


typedef AssetRef<Texture2D> Texture2DRef;
typedef AssetRef<Font> FontRef;


int main(int argc, const char **argv)
{
	bool result = false;
	Texture2D *texture1 = new Texture2D();
	Texture2D *texture2 = new Texture2D();
	
	result = AssetManager::Add("test_asset1", texture1);
	cout << "add result: " << (int)result << "  num assets: " << AssetManager::GetNumAssets() << endl;
	result = AssetManager::Add("test_asset2", texture2);
	cout << "add result: " << (int)result << "  num assets: " << AssetManager::GetNumAssets() << endl;
	result = AssetManager::Remove("test_asset1");
	cout << "remove result: " << (int)result << "  num assets: " << AssetManager::GetNumAssets() << endl;
	
	Font *ptr = AssetManager::Get<Font>("test_asset2");
	if(ptr)
		cout << "ptr is VALID" << endl;
	else
		cout << "ptr is NULL" << endl;
	
	Texture2DRef texRef;
	texRef = AssetManager::Get<Texture2D>("test_asset2");
	Texture2D *tex = AssetManager::Get<Texture2D>("test_asset2");
	if(tex)
		cout << "test_asset2 count: " << tex->GetReferenceCount() << endl;
	
	AssetManager::Flush();
	cout << "finished..." << "  num assets: " << AssetManager::GetNumAssets() << endl;
    return 0;
}

Using C++-11's new smart pointer system probably would have been a better way to go instead of supporting my own reference counting system, but I haven't figured them out yet. The ideas is that every different type of asset you'd have in your game (textures, models, definition files, etc.) would derive from Asset. Then, you'd create an AssetRef typedef variant for shorthand. An AssetRef is simply a pointer to the asset that's (hopefully) already loaded into the AssetManager.

 

Even though I don't assume all assets are loaded from files (for example, textures generated from rendering to a texture), I may add a filename field to Asset. I'm also thinking about adding an MD5 hash generated from the data stored in the asset, which the subclass is responsible for providing the data to hash. This is so if I wanted to create a visual editor like UE4 and Unity have, I can create an asset database that acts like a database in the sense that it keeps track of where everything is, or when files are moved in and out of the file project's root folder. Anyway, there's some funky scope to the editor side of things, and I haven't drawn up that namespace yet...

 

EDIT: The main() function at the bottom demonstrates how this currently works. I don't want any assets to be created without the AssetManager owning them though, but I also want it to be transparent to the end-programmer. I'm thinking about replacing AssetManager::Add() with a method like AssetManager::Load<T>(const char *filename, const char *filename) which can be specialized with its own loader code for every new Asset subclass that's created. I want AssetManager to own all assets allocated into memory, and do it transparently so its reference counting system works correctly. For generated assets, I'd like to override the new operator that'll allocate the asset, but automatically add the asset to the AssetManager like AssetManager::Add() does right now.

 

This is how I'd handle my game-ready content pipeline. AssetManager is intended to use models, textures, config files, etc that are serialized into some engine-ready binary blob setup, possibly combined into a file atlas (similar to how texture atlases), zip archive, or both. My engine will handle common image formats for things like textures and icons as well as a text-based, intermediate file format for models, but those are handled through the ContentProcessor pipeline, which is a separate namespace.




#5191259 Current state of custom and commercial game engines as of 2014

Posted by Vincent_M on 04 November 2014 - 09:45 PM

I've been wondering this myself. I work for a small studio developing freemium games for children using Unity, and it's a love-hate sort of thing. I'm really, really analytically when it comes to performance, and the fact that I can't see how things are being handled on the back-end drives me insane sometimes. The good part is though: I get to actually build games, and get paid for it. I was neither building games, or getting paid for that over a year ago... I was building an engine for fun fantasizing about making a game when I had the proper art team as a hobby.

 

That said, I think there are still benefits in at least attempting to build your engine, or using UE4 outside of using Unity exclusively. When you write your own engine or work with UE4, you're forced to work closer to the hardware than you'd normally would in Unity. I mean, there's plenty of utilization for 3D math, and the need to develop gameplay systems, but Unity hides a bunch of stuff pretty nicely. Sometimes, it's nice to understand how vertices are working under the hood, what a draw call really is, etc. Using Unity is certain a huge game-changer, but I think that it's also good to get back to the lower-level APIs Unity is build on top of (DirectX, OpenGL), and write your own math libraries. That's what'll get you really good, really fast. Then, you can take your new-found savvy-ness with you to Unity or UE4, and extend it to meet your specific needs much easier than before.

 

I still toil away at lower-level C++-based hobbyist engine stuff on my free time, though. I think it's good to learn this stuff from the ground up, and learn from first-hand experience. It makes me a better C++ programmer, and maybe I can make an engine that could compete with Unity that's also blazing fast itself. In the end, I'll understand what engine's doing under the hood because I've been under the hood. I believe that's what will help me get higher-paying programmer jobs as a senior-programmer in the long-run.

 

EDIT: Further more, I think we're entering an exciting time for graphics programming. AMD's Mantle is a more efficient API that DirectX 12 is getting close to. We have Apple's Metal, which is the Mantle of the mobile GPU world, but those are all proprietary technologies. Not that Microsoft or Apple don't have huge market-shares that investing in their APIs aren't worthwhile, but OpenGL's next-gen effort may provide a much more efficient API for accessing our GPUs. More-so, General-Purpose GPU programming, known as GPGPU is relatively new. OpenCL allows for parallel programming directly on GPUs, and NVIDIA's CUDA allows us to do the same. With this much freedom over the GPU, we finally have the leverage to start considering things like physics-based lighting techniques such as raytracing and Global Illumination with interactive frame rates. Unity 5 is already moving towards this by partnering up with a company in Texas I think called Geomerics who will provide their GI plugin, Enlighten, to Unity 5.

 

Not only is software hopefully getting a lot more efficient in using the provided hardware, but hardware's made huge leaps. NVIDIA just launched their new GTX 970 and 980 (non-titan right now), which is not only $200 cheaper than last year's Titans, but even faster. Earlier this year, they had some sort of demo where they had 19 of their network renderers (rumored to be $50,000 with > 32,000 CUDA cores a piece, so $950,000 USD of hardware, MSRP) was able to reproduce the collective processing power of the most powerful supercomputer documented in 2008 that originally cost $500,000,000 --in only 6 years.

 

That said, fast hardware is getting cheaper, and it's only going to get more exciting. Along with these new development, new innovations in game design and graphics processing will be possible. The algorithms will also get more complex, and in a few years, I think concepts that a lead engineer at Pixar had to know working on RenderMan 10 years ago will become commonly relevant in the real-time game development world.

 

I remember 10 years ago when I first started to learn C++ and DirectX. Things were simple. These "shader" things existed in DirectX 9c back in September, 2004, but they weren't required. In fact, my computer's GPU didn't support shaders at that time until I upgraded to an ATI Radeon that had 64MB of VRAM lol... If you wanted lighting, you just had to make some API calls, and the fixed-function magic made vertex lighting happened. It took me days to figure out why my large, lowpoly models weren't getting lit properly until I heard about per-pixel lighting.

 

That said, I would absolutely support others' efforts in developing their own engines. They'll either strike it rich like Unity did, make an admirable open source contribution to the the programming world like Blender, or at least acquire some serious graphics and software engineering skills that'll make that so much more effective at using commercial tools of any caliber.




#5191060 Datatype Size and Struct Compiler Padding

Posted by Vincent_M on 03 November 2014 - 11:40 PM

When it comes to storing my data in a game-ready format that's quick to load, I like to store my data using C's stdio library, so fread() and fwrite().There are a few problems with that, however:

-Changing the data structure will inevitably cause a crash when loading an older version of the file. Versioning can help here, or serializing the data in XML/JSON could help if it still wasn't so slow for large volumes of data on embedded devices.

-Compiler padding

 

I've noticed that some of my data types are odd numbers of bytes might pad the structure at different points in the struct's data. I think I've ran into issues where having a bool stored in a struct could cause issues too, but I usually store those in bit-fields if I have a group of them anyway. Are there any good guidelines I should follow when storing my data as binary blob files? I tend to write binary blob files for certain resources I serialize on my desktop computer in Visual Studio (Windows), or from Xcode (Mac), then load those files on any desktop OS or even mobile OS's like iOS and Android.

 

Also, is int the only datatype that changes its size based on processor? For example, int is usually 32-bit on 32-bit machines, and 64-bit on 64-bit machines. I tend to stay away from storing my integers as ints for this reason, and store my data as a long datatype as I'm pretty sure that it's always 32-bit whereas int is hardware-dependent. Is this correct so far?

 

If so, do any of C++'s other basic datatypes change their size? Also, is it wise to keep track of whether the CPU is little-endian or big-endian when writing files? I never really wrapped my head around that...




#5189557 General Programmer Salary

Posted by Vincent_M on 27 October 2014 - 06:43 PM

What's the average video game programmer's salary in the USA? Wikipedia (reliable source, right?!) says that some 2010 survey make anywhere between $72,000 (< 3 years experience) - $124,000 (6+ experience) at an average of $93,500. These numbers sound extremely high for me.




#5177683 COLLADA vs FBX... Are They Worthwhile for Generic Model Formats?

Posted by Vincent_M on 02 September 2014 - 10:57 AM

I'm excited to try it out. In the meantime, I setup the FBX SDK, and began exporting models from Blender (FBX 7.4). Surprisingly, the SDK with its constant API changes, still work. Btw, if I were to write a community-driven, open source, engine utilizing the FBX SDK, would I be able to distribute my engine? I understand that I wouldn't be able to distribute Autodesk's own SDK along with it, but if I were to distribute the built binaries on my end, would I have any issues there?

 

Btw Eric, I checked out your C4 Engine back in 2011, and it's impressive! It inspires me to become better at what I do.




#5177233 COLLADA vs FBX... Are They Worthwhile for Generic Model Formats?

Posted by Vincent_M on 31 August 2014 - 11:27 AM

I have seriously almost gone the COLLADA here and there since late 2012, but then I kept remembering how "well" I did with my .x parser, and how incompetent I was in grabbing data properly. That said, it's XML-based, and that's pretty easy to parse with an XML-parsing library, such tinyXML in C++. C# does have some good XML and JSON parsing abilities. I just wish we used XML more with our Unity projects at work lol. I've been hoping for some sort of person or group to push a new, tighter-spec'd format to replace COLLADA, and it looks like OpenGEX may be on the right track for that.

 

EDIT: Eric Lengyel, on 30 Aug 2014 - 9:34 PM, said:

snapback.png

These are just some of the reasons we created the Open Game Engine Exchange format (OpenGEX):

 

http://opengex.org/

When do you think you'll have an exporter ready for Blender? That's currently what I use since it's free, stable, highly-functional, and there are communities of artists out there with Blender models you can download for free. It'd be pretty easy to get a few models of Sintel with various animations to try different tests.






PARTNERS