コード例 #1
0
void GetGraphicsCardList(OVR::Array< OVR::String > &gpus)
{
    gpus.Clear();

    char info[256] = { 0 };
    FILE *file = popen("/usr/bin/lspci", "r");
    if (file)
    {
        int status = 0;
        while (status >= 0)
        {
            status = fscanf(file, "%*[^\n]\n"); // Read up till the end of the current line, leaving the file pointer at the beginning of the next line (skipping any leading whitespace).
            OVR_UNUSED(status);                 // Prevent GCC compiler warning: "ignoring return value of ‘int fscanf(FILE*, const char*, ...)"

            status = fscanf(file, "%*[^ ] VGA compatible controller: %255[^\n]", info);
            if (status == 1)
            {
                gpus.PushBack(String(info));
            }
        }
        pclose(file);
    }
    if (gpus.GetSizeI() <= 0)
    {
        gpus.PushBack(String("No video card details found."));
    }
}
コード例 #2
0
ファイル: JobManager.cpp プロジェクト: ejeinc/Meganekko
void ovrJobManagerImpl::Shutdown()
{
	Exiting = true;

	// allow all threads to complete their current job
	// waiting threads must timeout waiting for NewJobSignal
	while( Threads.GetSizeI() > 0 )
	{
		NewJobSignal->Raise(); // raise signal to release any waiting thread

		// loop through threads until we find one that's seen the Exiting flag and detatched
		int threadIndex = 0;
		for ( ; ; )
		{
			if ( Threads[threadIndex]->GetJni() == nullptr )
			{
				LOG( "Exited thread '%s'", Threads[threadIndex]->GetThreadName() );
				ovrJobThread::Destroy( Threads[threadIndex] );
				Threads.RemoveAtUnordered( threadIndex );
				break;
			}
			threadIndex++;
			if ( threadIndex >= Threads.GetSizeI() )
			{
				threadIndex = 0;
			}
		}
	}

	ovrSignal::Destroy( NewJobSignal );

	Initialized = false;
}
コード例 #3
0
ファイル: LocalPreferences.cpp プロジェクト: mec0825/VRLib
// Called on each resume, synchronously fetches the data.
void ovr_UpdateLocalPreferences()
{
#if defined( ENABLE_LOCAL_PREFS )	
	LocalPreferences.Clear();
	OVR::MemBufferFile	file( localPrefsFile );

	// Don't bother checking for duplicate names, later ones
	// will be ignored and removed on write.
	const char * txt = (const char *)file.Buffer;
	LOG( "%s is length %i", localPrefsFile, file.Length );
	for ( int ofs = 0 ; ofs < file.Length ; )
	{
		KeyPair	kp;
		int stopPos;
		kp.Key = ParseToken( txt, ofs, file.Length, stopPos );
		ofs = stopPos;
		kp.Value = ParseToken( txt, ofs, file.Length, stopPos );
		ofs = stopPos;

		if ( kp.Key.GetSize() > 0 )
		{
			LOG( "%s = %s", kp.Key.ToCStr(), kp.Value.ToCStr() );
			LocalPreferences.PushBack( kp );
		}
	}
#endif
}
コード例 #4
0
void VideoBrowser::OnMediaNotFound( OvrGuiSys & guiSys, String & title, String & imageFile, String & message )
{
	Videos.GetLocale().GetString( "@string/app_name", "@string/app_name", title );
	imageFile = "assets/sdcard.png";
	Videos.GetLocale().GetString( "@string/media_not_found", "@string/media_not_found", message );

	OVR::Array< OVR::String > wholeStrs;
	wholeStrs.PushBack( "Gear VR" );
	guiSys.GetDefaultFont().WordWrapText( message, 1.4f, wholeStrs );
}
コード例 #5
0
OVR::GlGeometry BuildFadedScreenMask( const float xFraction, const float yFraction )
{
	const float posx[] = { -1.001f, -1.0f + xFraction * 0.25f, -1.0f + xFraction, 1.0f - xFraction, 1.0f - xFraction * 0.25f, 1.001f };
	const float posy[] = { -1.001f, -1.0f + yFraction * 0.25f, -1.0f + yFraction, 1.0f - yFraction, 1.0f - yFraction * 0.25f, 1.001f };

	const int vertexCount = 6 * 6;

	OVR::VertexAttribs attribs;
	attribs.position.Resize( vertexCount );
	attribs.uv0.Resize( vertexCount );
	attribs.color.Resize( vertexCount );

	for ( int y = 0; y < 6; y++ )
	{
		for ( int x = 0; x < 6; x++ )
		{
			const int index = y * 6 + x;
			attribs.position[index].x = posx[x];
			attribs.position[index].y = posy[y];
			attribs.position[index].z = 0.0f;
			attribs.uv0[index].x = 0.0f;
			attribs.uv0[index].y = 0.0f;
			// the outer edges will have 0 color
			const float c = ( y <= 1 || y >= 4 || x <= 1 || x >= 4 ) ? 0.0f : 1.0f;
			for ( int i = 0; i < 3; i++ )
			{
				attribs.color[index][i] = c;
			}
			attribs.color[index][3] = 1.0f;	// solid alpha
		}
	}

	OVR::Array< OVR::TriangleIndex > indices;
	indices.Resize( 25 * 6 );

	// Should we flip the triangulation on the corners?
	int index = 0;
	for ( int x = 0; x < 5; x++ )
	{
		for ( int y = 0; y < 5; y++ )
		{
			indices[index + 0] = y * 6 + x;
			indices[index + 1] = y * 6 + x + 1;
			indices[index + 2] = (y + 1) * 6 + x;
			indices[index + 3] = (y + 1) * 6 + x;
			indices[index + 4] = y * 6 + x + 1;
			indices[index + 5] = (y + 1) * 6 + x + 1;
			index += 6;
		}
	}

	return OVR::GlGeometry( attribs, indices );
}
コード例 #6
0
//==============================
// BitmapFontLocal::WordWrapText
void BitmapFontLocal::WordWrapText( String & inOutText, const float widthMeters, OVR::Array< OVR::String > wholeStrsList, const float fontScale ) const
{
	float const xScale = FontInfo.ScaleFactor * fontScale;
	const int32_t totalLength = inOutText.GetLengthI();
	int32_t lastWhitespaceIndex = -1;
	double lineWidthAtLastWhitespace = 0.0f;
	double lineWidth = 0.0f;
	int dontSplitUntilIdx = -1;
	for ( int32_t pos = 0; pos < totalLength; ++pos )
	{
		uint32_t charCode = inOutText.GetCharAt( pos );

		// Replace any existing character escapes with space as we recompute where to insert line breaks
		if ( charCode == '\r' || charCode == '\n' || charCode == '\t' )
		{
			inOutText.Remove( pos );
			inOutText.InsertCharAt( ' ', pos );
			charCode = ' ';
		}

		FontGlyphType const & g = GlyphForCharCode( charCode );
		lineWidth += g.AdvanceX * xScale;

		for ( int i = 0; i < wholeStrsList.GetSizeI(); ++i )
		{
			int curWholeStrLen = wholeStrsList[i].GetLengthI();
			int endPos = pos + curWholeStrLen;

			if ( endPos < totalLength )
			{
				String subInStr = inOutText.Substring( pos, endPos );
				if ( subInStr == wholeStrsList[i] )
				{
					dontSplitUntilIdx = Alg::Max(dontSplitUntilIdx, endPos);
				}
			}
		}

		if ( pos >= dontSplitUntilIdx )
		{
			if ( charCode == ' ' )
			{
				lastWhitespaceIndex = pos;
				lineWidthAtLastWhitespace = lineWidth;
			}

			// always check the line width and as soon as we exceed it, wrap the text at
			// the last whitespace. This ensure's the text always fits within the width.
			if ( lineWidth >= widthMeters && lastWhitespaceIndex >= 0 )
			{
				dontSplitUntilIdx = -1;
				inOutText.Remove( lastWhitespaceIndex );
				inOutText.InsertCharAt( '\n', lastWhitespaceIndex );
				// subtract the width after the last whitespace so that we don't lose any
				// of the accumulated width since then.
				lineWidth -= lineWidthAtLastWhitespace;
			}
		}
	}
}
コード例 #7
0
ファイル: LocalPreferences.cpp プロジェクト: mec0825/VRLib
void ovr_ShutdownLocalPreferences()
{
#if defined( ENABLE_LOCAL_PREFS )	
	LOG( "ovr_ShutdownLocalPreferences" );
	LocalPreferences.ClearAndRelease();
#endif
}
コード例 #8
0
ファイル: JobManager.cpp プロジェクト: ejeinc/Meganekko
void ovrMPMCArray< T >::MoveArray( OVR::Array< T > & a )
{
	ovrScopedMutex mutex( ThisMutex );
	for ( int i = 0; i < A.GetSizeI(); ++i )
	{
		a.PushBack( A[i] );
	}
	A.Clear();
}
コード例 #9
0
ファイル: LocalPreferences.cpp プロジェクト: mec0825/VRLib
// Updates the in-memory data and synchronously writes it to storage.
void ovr_SetLocalPreferenceValueForKey( const char * keyName, const char * keyValue )
{
	LOG( "Set( %s, %s )", keyName, keyValue );

	int i = 0;
	for ( ; i < LocalPreferences.GetSizeI() ; i++ )
	{
		if ( !strcmp( keyName, LocalPreferences[i].Key.ToCStr() ) )
		{
			LocalPreferences[i].Value = keyValue;
		}
	}
	if ( i == LocalPreferences.GetSizeI() )
	{
		KeyPair	kp;
		kp.Key = keyName;
		kp.Value = keyValue;
		LocalPreferences.PushBack( kp );
	}

// don't write out if prefs are disabled because we could overwrite with default values
#if defined( ENABLE_LOCAL_PREFS )	
	// write the file
	FILE * f = fopen( localPrefsFile, "w" );
	if ( !f )
	{
		LOG( "Couldn't open %s for writing", localPrefsFile );
		return;
	}
	for ( int i = 0 ; i < LocalPreferences.GetSizeI() ; i++ )
	{
		fprintf( f, "%s %s", LocalPreferences[i].Key.ToCStr(), LocalPreferences[i].Value.ToCStr() );
	}
	fclose( f );
#endif
}
コード例 #10
0
ファイル: LocalPreferences.cpp プロジェクト: mec0825/VRLib
// Query the in-memory preferences for a key / value pair.
// If the returned string is not defaultKeyValue, it will remain valid until the next ovr_UpdateLocalPreferences().
const char * ovr_GetLocalPreferenceValueForKey( const char * keyName, const char * defaultKeyValue )
{
#if defined( ENABLE_LOCAL_PREFS )
	for ( int i = 0 ; i < LocalPreferences.GetSizeI() ; i++ )
	{
		if ( 0 == LocalPreferences[i].Key.CompareNoCase( keyName ) )
		{
			LOG( "Key %s = %s", keyName, LocalPreferences[i].Value.ToCStr() );
			return LocalPreferences[i].Value.ToCStr();
		}
	}
	LOG( "Key %s not found, returning default %s", keyName, defaultKeyValue );
#endif
	return defaultKeyValue;
}
コード例 #11
0
ファイル: JobManager.cpp プロジェクト: ejeinc/Meganekko
void ovrJobManagerImpl::Init( JavaVM & javaVM )
{
	Jvm = &javaVM;

	// signal must be created before any job threads are created
	NewJobSignal = ovrSignal::Create( true );

	// create all threads... they will end up waiting on a new job signal
	for ( int i = 0; i < MAX_THREADS; ++i )
	{
		char threadName[16];
		OVR_sprintf( threadName, sizeof( threadName ), "ovrJobThread_%i", i );

		ovrJobThread * jt = ovrJobThread::Create( this, i, threadName );
		Threads.PushBack( jt );
	}

	Initialized = true;
}
コード例 #12
0
//todo linux, this requires searching /var/ files
void GetGraphicsCardList(OVR::Array< OVR::String > &gpus)
{
    gpus.Clear();
}
コード例 #13
0
bool XmlHandler::ReadFile(const char* fileName, OVR::Render::RenderDevice* pRender,
	                      OVR::Render::Scene* pScene,
                          OVR::Array<Ptr<CollisionModel> >* pCollisions,
	                      OVR::Array<Ptr<CollisionModel> >* pGroundCollisions,
                          bool srgbAware /*= false*/,
                          bool anisotropic /*= false*/)
{
    if(pXmlDocument->LoadFile(fileName) != 0)
    {
        return false;
    }

    // Extract the relative path to our working directory for loading textures
    filePath[0] = 0;
    intptr_t pos = 0;
	intptr_t len = strlen(fileName);
    for(intptr_t i = len; i > 0; i--)
    {
        if (fileName[i-1]=='\\' || fileName[i-1]=='/')
        {
            memcpy(filePath, fileName, i);
            filePath[i] = 0;
            break;
        }        
    }    

    // Load the textures
	OVR_DEBUG_LOG_TEXT(("Loading textures..."));
    XMLElement* pXmlTexture = pXmlDocument->FirstChildElement("scene")->FirstChildElement("textures");
    OVR_ASSERT(pXmlTexture);
    if (pXmlTexture)
    {
        pXmlTexture->QueryIntAttribute("count", &textureCount);
        pXmlTexture = pXmlTexture->FirstChildElement("texture");
    }

    for(int i = 0; i < textureCount; ++i)
    {
        const char* textureName = pXmlTexture->Attribute("fileName");
		intptr_t    dotpos = strcspn(textureName, ".");
        char        fname[300];

		if (pos == len)
		{            
			OVR_sprintf(fname, 300, "%s", textureName);
		}
		else
		{
			OVR_sprintf(fname, 300, "%s%s", filePath, textureName);
		}

        int textureLoadFlags = 0;
        textureLoadFlags |= srgbAware ? TextureLoad_SrgbAware : 0;
        textureLoadFlags |= anisotropic ? TextureLoad_Anisotropic : 0;

        SysFile* pFile = new SysFile(fname);
		Ptr<Texture> texture;
		if (textureName[dotpos + 1] == 'd' || textureName[dotpos + 1] == 'D')
		{
			// DDS file
            Texture* tmp_ptr = LoadTextureDDSTopDown(pRender, pFile, textureLoadFlags);
			if(tmp_ptr)
			{
				texture.SetPtr(*tmp_ptr);
			}
		}
		else
		{
            Texture* tmp_ptr = LoadTextureTgaTopDown(pRender, pFile, textureLoadFlags, 255);
			if(tmp_ptr)
			{
				texture.SetPtr(*tmp_ptr);
			}
		}

        Textures.PushBack(texture);
		pFile->Close();
		pFile->Release();
        pXmlTexture = pXmlTexture->NextSiblingElement("texture");
    }
	OVR_DEBUG_LOG_TEXT(("Done.\n"));

    // Load the models
	pXmlDocument->FirstChildElement("scene")->FirstChildElement("models")->
		          QueryIntAttribute("count", &modelCount);
	
		OVR_DEBUG_LOG(("Loading models... %i models to load...", modelCount));
    XMLElement* pXmlModel = pXmlDocument->FirstChildElement("scene")->
		                                  FirstChildElement("models")->FirstChildElement("model");
    for(int i = 0; i < modelCount; ++i)
    {
		if (i % 15 == 0)
		{
			OVR_DEBUG_LOG_TEXT(("%i models remaining...", modelCount - i));
		}
        const char* name = pXmlModel->Attribute("name");
        Models.PushBack(*new Model(Prim_Triangles, name));
        bool isCollisionModel = false;
        pXmlModel->QueryBoolAttribute("isCollisionModel", &isCollisionModel);
        Models[i]->IsCollisionModel = isCollisionModel;
		if (isCollisionModel)
		{
			Models[i]->Visible = false;
		}

        bool tree_c = (strcmp(name, "tree_C") == 0) || (strcmp(name, "Object03") == 0);

        //read the vertices
        OVR::Array<Vector3f> *vertices = new OVR::Array<Vector3f>();
        ParseVectorString(pXmlModel->FirstChildElement("vertices")->FirstChild()->
			              ToText()->Value(), vertices);

		for (unsigned int vertexIndex = 0; vertexIndex < vertices->GetSize(); ++vertexIndex)
		{
			vertices->At(vertexIndex).x *= -1.0f;

            if (tree_c)
            {   // Move the terrace tree closer to the house
                vertices->At(vertexIndex).z += 0.5;
            }
		}

        //read the normals
        OVR::Array<Vector3f> *normals = new OVR::Array<Vector3f>();
        ParseVectorString(pXmlModel->FirstChildElement("normals")->FirstChild()->
			              ToText()->Value(), normals);

		for (unsigned int normalIndex = 0; normalIndex < normals->GetSize(); ++normalIndex)
		{
			normals->At(normalIndex).z *= -1.0f;
		}

        //read the textures
        OVR::Array<Vector3f> *diffuseUVs = new OVR::Array<Vector3f>();
        OVR::Array<Vector3f> *lightmapUVs = new OVR::Array<Vector3f>();
        int         diffuseTextureIndex = -1;
        int         lightmapTextureIndex = -1;
        XMLElement* pXmlCurMaterial = pXmlModel->FirstChildElement("material");

        while(pXmlCurMaterial != NULL)
        {
            if(pXmlCurMaterial->Attribute("name", "diffuse"))
            {
                pXmlCurMaterial->FirstChildElement("texture")->
					             QueryIntAttribute("index", &diffuseTextureIndex);
                if(diffuseTextureIndex > -1)
                {
                    ParseVectorString(pXmlCurMaterial->FirstChildElement("texture")->
						              FirstChild()->ToText()->Value(), diffuseUVs, true);
                }
            }
            else if(pXmlCurMaterial->Attribute("name", "lightmap"))
            {
                pXmlCurMaterial->FirstChildElement("texture")->
					                               QueryIntAttribute("index", &lightmapTextureIndex);
                if(lightmapTextureIndex > -1)
                {
                    XMLElement* firstChildElement = pXmlCurMaterial->FirstChildElement("texture");
                    XMLNode* firstChild = firstChildElement->FirstChild();
                    XMLText* text = firstChild->ToText();
                    const char* value = text->Value();
                    ParseVectorString(value, lightmapUVs, true);
                }
            }

            pXmlCurMaterial = pXmlCurMaterial->NextSiblingElement("material");
        }

        //set up the shader
        Ptr<ShaderFill> shader = *new ShaderFill(*pRender->CreateShaderSet());
        shader->GetShaders()->SetShader(pRender->LoadBuiltinShader(Shader_Vertex, VShader_MVP));
        if(diffuseTextureIndex > -1)
        {
            shader->SetTexture(0, Textures[diffuseTextureIndex]);
            if(lightmapTextureIndex > -1)
            {
                shader->GetShaders()->SetShader(pRender->LoadBuiltinShader(Shader_Fragment, FShader_MultiTexture));
                shader->SetTexture(1, Textures[lightmapTextureIndex]);
            }
            else
            {
                shader->GetShaders()->SetShader(pRender->LoadBuiltinShader(Shader_Fragment, FShader_Texture));
            }
        }
        else
        {
            shader->GetShaders()->SetShader(pRender->LoadBuiltinShader(Shader_Fragment, FShader_LitGouraud));
        }
        Models[i]->Fill = shader;

        //add all the vertices to the model
        const size_t numVerts = vertices->GetSize();
        for(size_t v = 0; v < numVerts; ++v)
        {
            if(diffuseTextureIndex > -1)
            {
                if(lightmapTextureIndex > -1)
                {
                    Models[i]->AddVertex(vertices->At(v).z, vertices->At(v).y, vertices->At(v).x, Color(255, 255, 255),
                                          diffuseUVs->At(v).x, diffuseUVs->At(v).y, lightmapUVs->At(v).x, lightmapUVs->At(v).y,
                                          normals->At(v).x, normals->At(v).y, normals->At(v).z);
                }
                else
                {
                    Models[i]->AddVertex(vertices->At(v).z, vertices->At(v).y, vertices->At(v).x, Color(255, 255, 255),
                                          diffuseUVs->At(v).x, diffuseUVs->At(v).y, 0, 0,
                                          normals->At(v).x, normals->At(v).y, normals->At(v).z);
                }
            }
            else
            {
                Models[i]->AddVertex(vertices->At(v).z, vertices->At(v).y, vertices->At(v).x, Color(255, 255, 255, 255),
                                      0, 0, 0, 0,
                                      normals->At(v).x, normals->At(v).y, normals->At(v).z);
            }
        }

        // Read the vertex indices for the triangles
        const char* indexStr = pXmlModel->FirstChildElement("indices")->
                                          FirstChild()->ToText()->Value();
        
        size_t stringLength = strlen(indexStr);

        for(size_t j = 0; j < stringLength; )
        {
            size_t k = j + 1;
            for(; k < stringLength; ++k)
            {
                if (indexStr[k] == ' ')
                    break;                
            }
            char text[20];
            for(size_t l = 0; l < k - j; ++l)
            {
                text[l] = indexStr[j + l];
            }
            text[k - j] = '\0';

            Models[i]->Indices.PushBack((unsigned short)atoi(text));
            j = k + 1;
        }

        // Reverse index order to match original expected orientation
        Array<uint16_t>& indices    = Models[i]->Indices;
        size_t         indexCount = indices.GetSize();         

        for (size_t revIndex = 0; revIndex < indexCount/2; revIndex++)
        {
            unsigned short itemp               = indices[revIndex];
            indices[revIndex]                  = indices[indexCount - revIndex - 1];
            indices[indexCount - revIndex - 1] = itemp;            
        }

        delete vertices;
        delete normals;
        delete diffuseUVs;
        delete lightmapUVs;

        pScene->World.Add(Models[i]);
        pScene->Models.PushBack(Models[i]);
        pXmlModel = pXmlModel->NextSiblingElement("model");
    }
	OVR_DEBUG_LOG(("Done."));

    //load the collision models
	OVR_DEBUG_LOG(("Loading collision models... "));
    XMLElement* pXmlCollisionModel = pXmlDocument->FirstChildElement("scene")->FirstChildElement("collisionModels");
    if (pXmlCollisionModel)
    {
		pXmlCollisionModel->QueryIntAttribute("count", &collisionModelCount);
        pXmlCollisionModel = pXmlCollisionModel->FirstChildElement("collisionModel");
    }

    XMLElement* pXmlPlane = NULL;
    for(int i = 0; i < collisionModelCount; ++i)
    {
        Ptr<CollisionModel> cm = *new CollisionModel();
        int planeCount = 0;
        
        OVR_ASSERT(pXmlCollisionModel != NULL); // collisionModelCount should guarantee this.
        if (pXmlCollisionModel)
        {
        pXmlCollisionModel->QueryIntAttribute("planeCount", &planeCount);

        pXmlPlane = pXmlCollisionModel->FirstChildElement("plane");
        for(int j = 0; j < planeCount; ++j)
        {
            Vector3f norm;
            pXmlPlane->QueryFloatAttribute("nx", &norm.x);
            pXmlPlane->QueryFloatAttribute("ny", &norm.y);
            pXmlPlane->QueryFloatAttribute("nz", &norm.z);
            float D;
            pXmlPlane->QueryFloatAttribute("d", &D);
            D -= 0.5f;
            if (i == 26)
                D += 0.5f;  // tighten the terrace collision so player can move right up to rail
            Planef p(norm.z, norm.y, norm.x * -1.0f, D);
            cm->Add(p);
            pXmlPlane = pXmlPlane->NextSiblingElement("plane");
        }

        if (pCollisions)
        pCollisions->PushBack(cm);
        pXmlCollisionModel = pXmlCollisionModel->NextSiblingElement("collisionModel");
    }
    }
	OVR_DEBUG_LOG(("done."));

    //load the ground collision models
	OVR_DEBUG_LOG(("Loading ground collision models..."));
    pXmlCollisionModel = pXmlDocument->FirstChildElement("scene")->FirstChildElement("groundCollisionModels");
    OVR_ASSERT(pXmlCollisionModel);
    if (pXmlCollisionModel)
    {
		pXmlCollisionModel->QueryIntAttribute("count", &groundCollisionModelCount);
        pXmlCollisionModel = pXmlCollisionModel->FirstChildElement("collisionModel");

    pXmlPlane = NULL;
    for(int i = 0; i < groundCollisionModelCount; ++i)
    {
        Ptr<CollisionModel> cm = *new CollisionModel();
        int planeCount = 0;
        pXmlCollisionModel->QueryIntAttribute("planeCount", &planeCount);

        pXmlPlane = pXmlCollisionModel->FirstChildElement("plane");
        for(int j = 0; j < planeCount; ++j)
        {
            Vector3f norm;
            pXmlPlane->QueryFloatAttribute("nx", &norm.x);
            pXmlPlane->QueryFloatAttribute("ny", &norm.y);
            pXmlPlane->QueryFloatAttribute("nz", &norm.z);
                float D = 0.f;
            pXmlPlane->QueryFloatAttribute("d", &D);
            Planef p(norm.z, norm.y, norm.x * -1.0f, D);
            cm->Add(p);
            pXmlPlane = pXmlPlane->NextSiblingElement("plane");
        }

        if (pGroundCollisions)
        pGroundCollisions->PushBack(cm);
        pXmlCollisionModel = pXmlCollisionModel->NextSiblingElement("collisionModel");
    }
    }
	OVR_DEBUG_LOG(("done."));
	return true;
}