A few weeks ago I purchased a MacBook Pro and later began the arduous process of porting the engine to iOS. My engine is organized into mini-engines, which are each their own projects (brought together into one master project) and build into separate libraries. The sound library, terrain library, physics library, etc. This keeps things highly modularized and ensures a logical connection between components. It would be similar to keeping each library in its own folder inside as part of the master project, except that it forcibly ensures that libraries can’t lazily #include files from outside their designated scope simply by adding another ../ to the #include path. It also makes it very clear how each library related to the other libraries.
But the biggest advantage is in porting. I spent 1 day porting all of the libraries (15 total) except for the graphics library, which took 4 days. Why is this so different from having one project with 15 folders? Because with only one project, you have to compile the whole project every time you compile. Xcode does not allow you to compile only a single translation unit. When I started porting, I copied the whole project over to the MacBook Pro and made an Xcode project. Then I made one new target for the “L. Spiro Engine Standard Library” which acts as the base library for all others, providing typedefs such as LSUINT32 and macros such as LSE_WINDOWS.
Then I added the files for that library and built it. It compiled out-of-the-box, but I took the time to enable warnings that are not available in Microsoft Visual Studio and fixed all of them.
Then repeated for the “L. Spiro Engine Math Library”. The advantage is that each time I added a new library I was able to compile and work with only the code for that library. Each library lives in its own little box and it makes it extremely easy to port the code.
The alternative is that you add one folder at a time into your master project, with each folder representing one of the libraries. When you compile you compile it all, and if you encounter any errors you don’t any say in the order in which you fix them. Xcode has a nasty habit of detecting one error and not detecting another until you fix the first. This happened to me when I added the graphics library, but I was free to ignore those errors if I wanted to hop over to the “L. Spiro Engine Sound Library” and fix those instead.
All-in-all the port was fairly painless. The only module that needed changing was the graphics module. A minor update to the L. Spiro Shader Language processor and all of my shaders were able to run in OpenGL ES 2.0. Without further ado, the iOS engine port screenshots.
L. Spiro
Looks nice, I can’t wait to try it ^^.
You know, Xcode does allow compiling a single translation unit, but why do you want that? Why not let it compile what needs to be compiled?
It would’ve been more interesting to hear about the porting process, stuff like ARM specifics (if any), input, etc. rather than how you set up the projects.
It would be more than helpful if you would share how to make Xcode compile only a single translation unit. Why? Because editing a single common header does not mean I want 455 translation units to compile when I am only modifying 1. I make changes to a .CPP and perhaps I need to edit its .H, but also some other .H that is shared by many other .CPP files. In fact right now I am adding a new type of memory allocator, and I have had to add a few defines to the main allocation header. I want to see if only the changes in my one .CPP file will compile. I don’t want to wait around for 20 minutes (it is not a joke—20 minutes with 7 MacBook Pros helping with distributed builds) while 454 unrelated .CPP files recompile.
It takes a lot of unnecessary time, and it should be fairly obvious now why the ability to compile a single translation unit is extremely important. So if you know, frankly I would be more interested in that than anything else.
I would even be willing to make a post about the difficulties in porting rather than project setup.
L. Spiro
About cross platform coding: I’m no expert in C++ (learned Java in college and use C# for my job), but from my research I’ve found what seems like two ways to handle a cross platform application (or graphics libraries).
1. Check for the operating system: #if defined(_WIN32) || defined(_WIN64) -> #define MY_WINDOWS, and have one for every OS, MY_MAC, MY_LINUX, etc. Then when code needs to differ by OS, you wrap them up in #ifdef MY_WINDOWS and handle them individually.
2. Have a single .h file as a sort of interface, and have each OS implemented by a different .cpp file.
From what I’ve gathered about your engine, is that you do the latter, and it’s also what I would LIKE to do, but I can’t seem to find any good material on how to do it. I mean I get I can have as many .cpp files implement a .h, I guess what I don’t know is how the compiler (if that’s even what handles it) knows which to use. It may also be that I just don’t know what to google to find an example.
If you know of any or can explain it that would be amazing!
At work we use #2. I prefer to strictly maintain the 1-to-1 relationship between .CPP files and header files so I do this (an example using a texture class):
Base class:
LSGTexture2dBase.cpp | LSGTexture2dBase.h -> CTexture2dBase
Middle classes (all inherit from CTexture2dBase).
LSGDirectX11Texture2d.cpp | LSGDirectX11Texture2d.h -> CDirectX11Texture2d
LSGDirectX9Texture2d.cpp | LSGDirectX9Texture2d.h -> CDirectX9Texture2d
LSGOpenGlTexture2d.cpp | LSGOpenGlTexture2d.h -> COpenGlTexture2d
LSGOpenGlEsTexture2d.cpp | LSGOpenGlEsTexture2d.h -> COpenGlEsTexture2d
Top class:
LSGTexture2d.cpp | LSGTexture2d.h -> CTexture2d
CTexture2d inherits from one of the base classes as follows:
CTexture2d : public
#if defined( LSE_DX11 )
CDirectX11Texture2d
#elif defined( LSE_DX9 )
CDirectX9Texture2d
#elif defined( LSE_DX9 )
COpenGlTexture2d
#elif defined( LSE_DX9 )
COpenGlEsTexture2d
#endif
To answer your question, the compiler tries to compile all of them.
This is not a problem because LSGDirectX11Texture2d.cpp is a blank file unless LSE_DX11 is defined.
LSGDirectX11Texture2d.cpp:
#include "LSGDirectX11Texture2d.h"
#ifdef LSG_DX11
namespace lsg {
// == Various constructors.
LSE_CALLCTOR CDirectX11Texture2d::CDirectX11Texture2d() :
m_ptTexture( NULL ),
m_rtvRenderTargetView( NULL ),
m_psrvShaderView( NULL ) {
}
LSE_CALLCTOR CDirectX11Texture2d::~CDirectX11Texture2d() {
Reset();
}
…
There is no magical way to tell the compiler which .CPP file to compile; it compiles all of them. You just use the same macros as before you selectively include their contents.
L. Spiro
Hi Spiro,
It is very nice your engine.
What tool or library do you use for the sound in iOS?
And, do you have your own mesh’s loader or use a 3rd party library?
Thanks andvanced
OpenAL is used for sound. Model converter (FBX -> LSM), loading, renderer, etc., all proprietary and from-scratch. A model format is one of the easiest things to create and I see no reason to use any 3rd-party libraries/code for it.
L. Spiro