Jump to content

  • Log In with Google      Sign In   
  • Create Account

We're offering banner ads on our site from just $5!

1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


TheComet

Member Since 02 Oct 2013
Offline Last Active Yesterday, 05:21 PM

Topics I've Started

O(pow(N,12))

22 October 2014 - 04:01 PM

#include "main.h"
#include "renderSystem.h"
#include "largeAsteroid.h"

// To future Karl, or anybody who may have the joy of upkeeping this code.
// I am truly sorry.
#define ASTEROID_SIZE 50

AsteroidVoxel::AsteroidVoxel() :
	mExist( false )
{
}

AsteroidVoxelVertex::AsteroidVoxelVertex()
{
	mExist = true;
}

void AsteroidVoxelVertex::setVertex( u32 vertex )
{
	mVertex = vertex;
}

AsteroidVoxelVertex::~AsteroidVoxelVertex()
{
}

AsteroidData::AsteroidData()
{

	//This part is a pain, but initialize the mVoxelData array
	for( s32 x = 0; x < ASTEROID_SIZE; x++ )
	{
		for( s32 y = 0; y < ASTEROID_SIZE; y++ )
		{
			for( s32 z = 0; z < ASTEROID_SIZE; z++ )
			{
				mVoxelData[x][y][z].mExist = false;
			}
		}
	}

}

AsteroidData::~AsteroidData()
{

}

void AsteroidData::genTestShape()
{
	for( s32 x = 2; x < ASTEROID_SIZE - 2 ; x++ )
		for( s32 y = 2; y < ASTEROID_SIZE - 2; y++ )
			for( s32 z = 2; z < ASTEROID_SIZE - 2; z++ )
				mVoxelData[x][y][z].mExist = true;
}

void AsteroidData::generateBaseShape()
{
	// Set up random number generator.
	mt19937 generator;
	generator.seed( gRenderSystem->getOgreRoot()->getTimer()->getMilliseconds() * rand() % 100000 );

	Vec3 center( ASTEROID_SIZE / 2.0f , ASTEROID_SIZE / 2.0f, ASTEROID_SIZE / 2.0f );
	for( s32 x = 0; x < ASTEROID_SIZE; x++ )
		for( s32 y = 0; y < ASTEROID_SIZE; y++ )
			for( s32 z = 0; z < ASTEROID_SIZE; z++ )
				if( ( Vec3( x, y, z ) - center ).LengthSq() < ( ( ASTEROID_SIZE / 2.0f ) * ( ASTEROID_SIZE / 2.0f ) ) )
					mVoxelData[x][y][z].mExist = true;

	//Add craters.
	uniform_int_distribution<s32> craterImpacts( 2, 10 );
	uniform_real_distribution<f32> craterPosition( 0.0f, ASTEROID_SIZE );
	uniform_real_distribution<f32> craterRadius( ASTEROID_SIZE / 8.0f, ASTEROID_SIZE / 4.0f );

	u32 noCraterImpacts = craterImpacts( generator );

	for( u32 i = 0; i < noCraterImpacts; ++i )
	{
		Vec3 origin( craterPosition( generator ), craterPosition( generator ), craterPosition( generator ) );
		f32 lCraterRadius = craterRadius( generator );

		for( u32 x = 0; x < ASTEROID_SIZE; x++ )
			for( u32 y = 0; y < ASTEROID_SIZE; y++ )
				for( u32 z = 0; z < ASTEROID_SIZE; z++ )
					if( ( Vec3( x, y, z ) - origin ).LengthSq() < lCraterRadius * lCraterRadius ) {}
		//mVoxelData[x][y][z].mExist = false;
	}

}

void AsteroidData::generateIndexVertexBuffers()
{
	// Eliminate non-visible voxels
	// Check each of the 6 sides.
	vector<AsteroidVoxelVertex*> mVisibleVoxels;
	AsteroidVoxelVertex* mVisVoxelStructure[ASTEROID_SIZE][ASTEROID_SIZE][ASTEROID_SIZE];

	u32 vertexCount = 0;

	for( s32 x = 0; x < ASTEROID_SIZE; x++ )
	{
		for( s32 y = 0; y < ASTEROID_SIZE; y++ )
		{
			for( s32 z = 0; z < ASTEROID_SIZE; z++ )
			{

				bool canBeSeen = 0;

				if( x == 0 || x == ASTEROID_SIZE - 1 || y == 0 || y == ASTEROID_SIZE - 1 || z == 0 || z == ASTEROID_SIZE - 1 )
					if( mVoxelData[x][y][z].mExist == true )
						canBeSeen = true;

				if( x > 0 )
					if( mVoxelData[x - 1][y][z].mExist == false )
						canBeSeen = true;

				if( y > 0 )
					if( mVoxelData[x][y - 1][z].mExist == false )
						canBeSeen = true;

				if( z > 0 )
					if( mVoxelData[x][y][z - 1].mExist == false )
						canBeSeen = true;

				if( x < ASTEROID_SIZE - 1 )
					if( mVoxelData[x + 1][y][z].mExist == false )
						canBeSeen = true;

				if( y < ASTEROID_SIZE - 1 )
					if( mVoxelData[x][y + 1][z].mExist == false )
						canBeSeen = true;

				if( z < ASTEROID_SIZE - 1 )
					if( mVoxelData[x][y][z + 1].mExist == false )
						canBeSeen = true;

				if( canBeSeen )
				{
					if( mVoxelData[x][y][z].mExist == true )
					{
						// Set vertex
						AsteroidVoxelVertex* vertex = new AsteroidVoxelVertex;
						vertex->setVertex( vertexCount );

						//Add to data structures
						mVisibleVoxels.push_back( vertex );
						mVisVoxelStructure[x][y][z] = vertex;

						// Add to vertex list
						mVertex.push_back( f32( x ) * 100.0 );
						mVertex.push_back( f32( y ) * 100.0 );
						mVertex.push_back( f32( z ) * 100.0 );

						vertexCount++;
					}
					else
					{
						AsteroidVoxelVertex* vertex = new AsteroidVoxelVertex;
						vertex->mExist = false;

						mVisibleVoxels.push_back( vertex );
						mVisVoxelStructure[x][y][z] = vertex;
					}
				}
				else
				{
					AsteroidVoxelVertex* vertex = new AsteroidVoxelVertex;
					vertex->mExist = false;

					mVisibleVoxels.push_back( vertex );
					mVisVoxelStructure[x][y][z] = vertex;
				}
			}
		}
	}

	// Use brute force against the VoxelCloud to generate index.

	for( u32 x = 0; x < ASTEROID_SIZE - 1; x ++ )
	{
		for( u32 y = 0; y < ASTEROID_SIZE - 1; y++ )
		{
			for( u32 z = 0; z < ASTEROID_SIZE - 1; z++ )
			{
				for( u32 basex = x; basex <= x + 1; basex++ )
					for( u32 basey = y; basey <= y + 1; basey++ )
						for( u32 basez = z; basez <= z + 1; basez++ )
							// Find all triangles
							// Find second vertex, then find the next vertex using the second vertex as a starting point
							for( u32 ox = 0; ox <= 1; ox++ )
							{
								for( u32 oy = 0; oy <= 1; oy++ )
								{
									for( u32 oz = 0; oz <= 1; oz++ )
									{
										for( s32 ox1 = -1; ox1 <= 1; ox1++ )
										{
											for( s32 oy1 = - 1; oy1 <= 1; oy1++ )
											{
												for( s32 oz1 = - 1; oz1 <= 1; oz1++ )
												{
													if( basex + ox1 + ox >= x )
														if( basex + ox1 + ox <= x + 1 )
															if( basey + oy1 + oy >= y )
																if( basey + oy1 + oy <= y + 1 )
																	if( basez + oz1 + oz >= z )
																		if( basez + oz1 + oz <= z + 1 )
																			if( basex + ox1 + ox <= x + 1 )
																				if( basey + oy1 + oy <= y + 1 )
																					if( basez + oz + oz1 <= z + 1 )
																						if( basex + ox <= x + 1 )
																							if( basey + oy <= y + 1 )
																								if( basez + oz <= z + 1 )
																									if( mVisVoxelStructure[basex][basey][basez]->mExist == true )
																										if( mVisVoxelStructure[basex + ox][basey + oy][basez + oz]->mExist == true )
																											if( mVisVoxelStructure[basex + ox + ox1][basey + oy + oy1][basez + oz + oz1]->mExist == true )
																												if( mVisVoxelStructure[basex][basey][basez]->mVertex != mVisVoxelStructure[basex + ox][basey + oy][basez + oz]->mVertex )
																													if( mVisVoxelStructure[basex][basey][basez]->mVertex != mVisVoxelStructure[basex + ox][basey + oy][basez + oz]->mVertex )
																														if( mVisVoxelStructure[basex + ox][basey + oy][basez + oz]->mVertex != mVisVoxelStructure[basex + ox + ox1][basey + oy + oy1][basez + oz + oz1]->mVertex )
																														{
																															mIndex.push_back( mVisVoxelStructure[basex + ox + ox1][basey + oy + oy1][basez + oz + oz1]->mVertex );
																															mIndex.push_back( mVisVoxelStructure[basex + ox][basey + oy][basez + oz]->mVertex );
																															mIndex.push_back( mVisVoxelStructure[basex][basey][basez]->mVertex );

																															mIndex.push_back( mVisVoxelStructure[basex + ox + ox1][basey + oy + oy1][basez + oz + oz1]->mVertex );
																															mIndex.push_back( mVisVoxelStructure[basex][basey][basez]->mVertex );
																															mIndex.push_back( mVisVoxelStructure[basex + ox][basey + oy][basez + oz]->mVertex );

																														}

												}
											}
										}
									}
								}
							}

			}
		}
	}

	//Clear up after myself.
	for( auto i = mVisibleVoxels.begin(); i != mVisibleVoxels.end(); ++i )
	{
		delete(*i);
	}

	mVisibleVoxels.empty();

}


////////////////////////////////////////////////////
// Divide between AsteroidData and LargeAsteroid //
////////////////////////////////////////////////////


LargeAsteroid::LargeAsteroid() :
	mMeshData( nullptr ),
	mSceneNode( nullptr )
{

	mMeshData = new AsteroidData;

	mMeshData->generateBaseShape();
	mMeshData->generateIndexVertexBuffers();

	// set up mesh, vertex decl, entity and scene node.
	mMesh = Ogre::MeshManager::getSingleton().createManual( generateName(), "General" );
	mSubMesh = mMesh->createSubMesh();

	mMesh->sharedVertexData = new Ogre::VertexData;
	mMesh->sharedVertexData->vertexCount = mMeshData->mVertex.size();

	mDecl = mMesh->sharedVertexData->vertexDeclaration;

	size_t offset = 0;
	mDecl->addElement( 0, offset, Ogre::VET_FLOAT3, Ogre::VES_POSITION );
	offset += Ogre::VertexElement::getTypeSize( Ogre::VET_FLOAT3 );

	mVBuf = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer( offset, mMeshData->mVertex.size(), Ogre::HardwareBuffer::HBU_DYNAMIC, false );
	mIBuf = Ogre::HardwareBufferManager::getSingleton().createIndexBuffer( Ogre::HardwareIndexBuffer::IT_32BIT, mMeshData->mIndex.size(), Ogre::HardwareBuffer::HBU_DYNAMIC );

	mMesh->sharedVertexData->vertexBufferBinding->setBinding( 0, mVBuf );
	mSubMesh->indexData->indexBuffer = mIBuf;
	mSubMesh->indexData->indexCount = mMeshData->mIndex.size();
	mSubMesh->indexData->indexStart = 0;

	//lock buffers
	f32* vertices = static_cast<f32*>( mVBuf->lock( Ogre::HardwareBuffer::HBL_DISCARD ) );

	u32 vx = 0;

	for( auto i = mMeshData->mVertex.begin(); i != mMeshData->mVertex.end(); ++i )
	{
		vertices[vx] = (*i);
		vx++;
	}

	u32* indices = static_cast<u32*>( mIBuf->lock( Ogre::HardwareBuffer::HBL_DISCARD ) );

	vx = 0;
	for( auto i = mMeshData->mIndex.begin(); i != mMeshData->mIndex.end(); ++i )
	{
		indices[vx] = (*i);
		vx++;
	}

	//unlock buffers
	mVBuf->unlock();
	mIBuf->unlock();


	mMesh->_setBounds( Ogre::AxisAlignedBox( 0, 0, 0, ASTEROID_SIZE * 100.0f, ASTEROID_SIZE * 100.0f, ASTEROID_SIZE * 100.0f ) );
	mMesh->load();

	mEntity = gRenderSystem->getSceneMgr()->createEntity( generateName(), mMesh );
	mSceneNode = gRenderSystem->getRootSceneNode()->createChildSceneNode();
	mSceneNode->attachObject( mEntity );

	gRenderSystem->getSceneMgr()->setAmbientLight( Ogre::ColourValue( 0.5, 0.5, 0.5f ) );

}

LargeAsteroid::~LargeAsteroid()
{
	gRenderSystem->getSceneMgr()->destroyEntity( mEntity );
	gRenderSystem->getSceneMgr()->destroySceneNode( mSceneNode );

	mMesh.setNull();
	mVBuf.setNull();
	mIBuf.setNull();

	//Free up memory.
	delete mMeshData;


}

cmake linking errors

17 October 2014 - 08:18 AM

I created a project demonstrating the problem I'm having: https://github.com/TheComet93/cmake-sucks

 

I have a library which links against python and boost_python. The library compiles fine, but if I try to link against my library from an executable, I get these linking errors:

$ make
[ 50%] Built target library
Linking CXX executable app
../../cmake-dep/lib/libboost_python.so: undefined reference to `PyBytes_Size'
../../cmake-dep/lib/libboost_python.so: undefined reference to `PyUnicode_FromString'
../../cmake-dep/lib/libboost_python.so: undefined reference to `PyUnicode_FromFormat'
../../cmake-dep/lib/libboost_python.so: undefined reference to `PyBytes_AsString'
../../cmake-dep/lib/libboost_python.so: undefined reference to `PyUnicode_InternFromString'
../../cmake-dep/lib/libboost_python.so: undefined reference to `PyUnicode_FromEncodedObject'
../../cmake-dep/lib/libboost_python.so: undefined reference to `PyUnicode_AsWideChar'
../../cmake-dep/lib/libboost_python.so: undefined reference to `PyModule_Create2'
../../cmake-dep/lib/libboost_python.so: undefined reference to `PyUnicode_FromStringAndSize'
../../cmake-dep/lib/libboost_python.so: undefined reference to `PyUnicode_AsUTF8String'
../../cmake-dep/lib/libboost_python.so: undefined reference to `PyUnicode_AsUTF8'
collect2: error: ld returned 1 exit status
app/CMakeFiles/app.dir/build.make:91: recipe for target 'app/app' failed
make[2]: *** [app/app] Error 1
CMakeFiles/Makefile2:160: recipe for target 'app/CMakeFiles/app.dir/all' failed
make[1]: *** [app/CMakeFiles/app.dir/all] Error 2
Makefile:75: recipe for target 'all' failed
make: *** [all] Error 2

Enabling verbose output reveals the core of the issue:

/usr/bin/c++       CMakeFiles/app.dir/main.cpp.o  -o app  -L/home/thecomet/documents/programming/c++/cmake-sucks/cmake-dep/lib -rdynamic ../library/liblibrary.a -lpython2.7 ../../cmake-dep/lib/libboost_python.so -Wl,-rpath,/home/thecomet/documents/programming/c++/cmake-sucks/cmake-dep/lib

liblibrary.a is being linked before python2.7 and boost_python, which shouldn't be the case.

 

I'm stuck at how I'm supposed to fix this.

 

If you look at the CMakeLists.txt files, you'll see I'm basically just doing:

add_library(library ${files})
target_link_library(library python boost_python)

and:

add_executable(app ${files})
target_link_library(app library)

This is already enough to generate these linker errors.


Multithreading issues: Unit test fails sometimes

02 October 2014 - 04:36 AM

I'm at a loss here because I don't know how to debug this. Failure occurs on the line ASSERT_EQ at the bottom of the test case.
 
The test works most of the time, but has a 30% chance of producing the following error:
 
tests/src/test_StressTests.cpp:58: Failure
Value of: entity.getComponent<Position>()
  Actual: Position(1994, 1000)
Expected: Position(2000, 1000)
How is this possible? How can position.x be updated three times less than position.y? Sometimes it's also Position(1998, 1000) or Position(1996, 1000). The only thing I can observe is that position.y is never incorrect.

 

This is my test case:
#include <gmock/gmock.h>
#include <ontology/Ontology.hpp>
#include <math.h>

#define NAME Stress

using namespace Ontology;

// ----------------------------------------------------------------------------
// test fixture
// ----------------------------------------------------------------------------

struct Position : public Component
{
    Position(int x, int y) : x(x), y(y) {}
    unsigned int x, y;
};
inline bool operator==(const Position& lhs, const Position rhs)
{
    return lhs.x == rhs.x && lhs.y == rhs.y;
}


struct Movement : public System
{
    void initialise() override {}
    void processEntity(Entity& e) override
    {
        e.getComponent<Position>().x += 2;
        e.getComponent<Position>().y += 1;
        for(volatile int i = 0; i != 10000; ++i)
            sqrt(938.523);
    }
};

// ----------------------------------------------------------------------------
// tests
// ----------------------------------------------------------------------------

TEST(NAME, ThousandEntities)
{
    World world;
    world.getSystemManager()
        .addSystem<Movement>()
        .initialise()
        ;
    for(int i = 0; i != 1000; ++i)
        world.getEntityManager().createEntity("entity")
            .addComponent<Position>(0, 0)
            ;
    // udpate world 1000 times
    for(int i = 0; i != 1000; ++i)
        world.update();


    // all entities should have moved to 1000*[2,1] = [2000, 1000]
    for(auto& entity : world.getEntityManager().getEntityList())
    {
        ASSERT_EQ(Position(2000, 1000), entity.getComponent<Position>());
    }
}
 

 
Could someone take a look at the way I'm dispatching the worker threads and tell me if they spot something blatantly obvious? The relevant sections of code are as follows.
 
World class creates the thread pool as follows:
 
World::World() :
    m_IoService(),
    m_ThreadPool(),
    m_Work(m_IoService)
{
    // populate thread pool with as many threads as there are cores
    for(int i = 0; i != getNumberOfCores(); ++i)
        m_ThreadPool.create_thread(
            boost::bind(&boost::asio::io_service::run, &m_IoService)
        );
}
Where m_IoService, m_ThreadPool and m_Work are declared as:
 
    boost::asio::io_service         m_IoService;
    boost::thread_group             m_ThreadPool;
    boost::asio::io_service::work   m_Work;
World::update() calls SystemManager::update(). SystemManager holds a vector of System object pointers.
 
void SystemManager::update()
{
    for(const auto& system : m_ExecutionList)
        system->update();
}
System::update() pushes as many System::processEntity calls into the worker queue as there are entities (in this case 1000). System::processEntity is pure virtual.
 
        this->world->getIoService().post(
                boost::bind(&System::processEntity, this, boost::ref(it))
        );
        // wait for all entities to be processed
        this->world->getIoService().poll();
I have a hunch that the very last line, this->world->getIoService().poll(), only waits until the queue is empty instead of waiting for all worker threads to be idle (that's actually the behaviour I intend but I couldn't find any way to do this). This being the case, the test case could finish asserting all of the expected entity positions before the last few entities' positions are actually processed.
 
It doesn't explain why the X component of Position is screwed while the Y component is always fine.

[EDIT] In case someone wishes to obtain the project and run it themselves:
https://www.github.com/TheComet93/ontology
git checkout optimise
mkdir build && cd build
cmake -DBUILD_TESTS=ON ..

Some move semantic confusion

01 October 2014 - 04:16 PM

Consider the following code:

struct Crap {};

struct Thing
{
    void addCrap()
    {
        m_Crap.push_back(std::move(Crap()));
    }
private:
    std::vector<Crap> m_Crap;
};

In my mind, after the push_back call, m_Crap is holding an invalid Crap object. Isn't Crap() temporarily allocated on the stack, moved into the vector, and then deallocated, resulting in m_Crap referencing a memory block on the stack?

 

Am I also correct to assume that the following is invalid as well?

struct Crap {};

struct Thing
{
    void addCrap()
    {
        Crap crap;
        m_Crap.push_back(std::move(crap));
    }
private:
    std::vector<Crap> m_Crap;
};

Threading question

30 September 2014 - 01:59 AM

I'm trying to learn more about multithreading and am a little confused.

 

Currently, I'm using Ogre3D and I'm processing my game entities asynchronously. This means each entity is writing to Ogre::SceneNode asynchronously whenever its position changes:

void processMyGameEntity(Entity& e)
{
    Vector3 newPosition = doFancyMovementCode(e);
    e.sceneNode->setPosition(newPosition);
}

// ... elsewhere
void Game::updateGame()
{
    for(auto& entity : m_EntityList)
        this->threadPool.push(processMyGameEntity(entity));
}

I know Ogre3D is using boost.thread in the background for rendering, so it is asynchronously reading from scene nodes, but I'm not synchronising when writing to the scenenodes. Why is this working fine? Is it safe for me to continue doing this?

 

Another question I have: The thread pool I'm using uses std::thread, but Ogre3D is using boost::thread. How do I synchronise objects being shared between the two?


PARTNERS