Scene node contained in object

Problems building or running the engine, queries about how to use features etc.
Protagonist
Goblin
Posts: 214
Joined: Tue Nov 21, 2006 11:11 pm

Scene node contained in object

Post by Protagonist »

Hi there, I'm developing a submarine simulator, and have got a little question.

For the moment, I'm just creating a submarine and a target ship

I've created the scene node for the sub, so that I have a mesh, in the location that I want, etc, however, I obviously also want a submarine class to hold data such as maxSpeed, health_points, etc, as well as member functions to raise and lower the periscope etc (because I want to time how long it's raised for)

I wasn't quite sure the best way of going about doing this.
Would the following work?

create a Submarine class, which holds a pointer to a scene node as a data member. Use a setSceneNode member function to set the pointer to the previously created submarine scene node.
Then use a getSceneNode member function, which simply returns the stored pointer to the scene node...

so I can use commands like sub->getSceneNode->getPosition to get the position of the sub scene node which is pointed to by the data member of my sub object (of class Sub)

would that be possible?

Or could the actual sub object identifier be a pointer to the sub scene node, so I can do sub->getPosition?

Any advice would be very welcome.
Thanks a lot.
User avatar
Kencho
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4011
Joined: Fri Sep 19, 2003 6:28 pm
Location: Burgos, Spain
x 2

Post by Kencho »

The second is not desirable in my opinion. Designing/finding classes is a matter of classifying responsibilities. A scene node is just an anchor, where you can attach a movable object into the scene itself. Your submarine class is instead a game logic entity. It stores and takes responsibility of anything submarine-related (or mostly anything). So you should keep a pointer of the scene node in the submarine class. Also, I suggest you to create and destroy the scene node, entity and such from inside the submarine class, as it's its responsibility. That's what composition relationships mean (that scene node is part of the submarine itself, in terms of game logic).

Hope that helps
Image
Protagonist
Goblin
Posts: 214
Joined: Tue Nov 21, 2006 11:11 pm

Post by Protagonist »

Thanks for such a fast reply :)
I really need to get this implemented tonight.
Cool, I agree....
however, I'm a bit lost as to how to implement that now.

I tried having the following data member:
SceneNode *ptrNode = 0;

and these member functions:
void setSceneNode( SceneNode value )
{
ptrNode = value;
}

SceneNode getSceneNode ()
{
return ptrNode;
}

so I can do this in the createScene(void) function:

sub.getSceneNode( nodeSub );
sub.getSceneNode()->setPosition( 0, -30, 0 );

where nodeSub is the scene node to which the sub mesh is attached,

and where sub is an object of type Submarine in which the getSceneNode function etc are defined

However, that's not legal...
Do you have any suggestions for how to implement it correctly?

Thanks very much
User avatar
Kencho
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4011
Joined: Fri Sep 19, 2003 6:28 pm
Location: Burgos, Spain
x 2

Post by Kencho »

Pass the scene manager as a parameter to the submarine's constructor, and create the scene node and entity yourself. And of course, destroy both in the destructor ;)
You might also consider providing "façade" methods to the submarine, to only play with things such as set position, set yaw/pitch... instead of letting access to the whole scene node operations
Image
Protagonist
Goblin
Posts: 214
Joined: Tue Nov 21, 2006 11:11 pm

Post by Protagonist »

Thanks a lot!

I think I get that, I'll implement it soon. :)

thanks
User avatar
Kencho
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4011
Joined: Fri Sep 19, 2003 6:28 pm
Location: Burgos, Spain
x 2

Post by Kencho »

Splitted this topic in two. The new branch is here: http://www.ogre3d.org/phpBB2/viewtopic. ... 837#191837
Try not to change the overall topic. Open a new thread if you need to solve a different problem.
Image
Protagonist
Goblin
Posts: 214
Joined: Tue Nov 21, 2006 11:11 pm

Post by Protagonist »

Kencho wrote:Pass the scene manager as a parameter to the submarine's constructor, and create the scene node and entity yourself. And of course, destroy both in the destructor ;)
You might also consider providing "façade" methods to the submarine, to only play with things such as set position, set yaw/pitch... instead of letting access to the whole scene node operations
Hey, that was a great idea! It's really coming together I think. Thanks a lot.
I've got a problem though... I think it's just a C++ misunderstanding of mine...

I get an error that the constructor of my derived class Sub from base class Watercraft cannot have a default constructor.

I'll post an accurate error message if that makes no sense (can't compile it where I am)

The code is as follows:

Code: Select all

#include <iostream>
#include <ctime>
#include <cmath>

using namespace std;

#ifndef __Simulator_H__ //1
#define __Simulator_H__

#include "Ogre.h"
#include "OgreConfigFile.h"
#include "ExampleFrameListener.h"

using namespace Ogre;


class Watercraft
{
public:
	Watercraft( String &entityName, String ptype, SceneManager *mSceneMgr, 
		int healthValue, int maxSpeedValue, int typicalSpeedValue )
	{
		initialiseObject( entityName, ptype, mSceneMgr );
		setHealth( healthValue );
		setMaxSpeed( maxSpeedValue );
		setTypicalSpeed( typicalSpeedValue );
	}
	~Watercraft()
	{
		 
	}

	void setHealth( int value )
	{
		health = value;
	}

	void setMaxSpeed( int value )
	{
		maxSpeed = value;
	}

	void setTypicalSpeed( int value )
	{
		typicalSpeed = value;
	}

	int getHealth()
	{
		return health;
	}

	int getMaxSpeed()
	{
		return maxSpeed;
	}

	int getTypicalSpeed()
	{
		return typicalSpeed;
	}

	//setPosition facade method
	void setPosition( Real x, Real y, Real z )
	{
		node->setPosition( x, y, z );
	}

	const Vector3& getPosition()
	{
		return node->getPosition();
	}
		
	void faceTarget( Watercraft *target )
	{
		//get position of target
		Vector3 targetPosition = target->getPosition();
		//node->setFixedYawAxis( true ); //Vector3::UNIT_Y

		//make the watercraft face the target
		this->lookAt( targetPosition, Node::TS_PARENT ); 
		//TS - transform space, relative to parent
		//3rd param = The vector which normally describes 
		//the natural direction of the node, optional
	}

	void lookAt( const Vector3 &targetPoint, Ogre::Node::TransformSpace relativeTo, 
		const Vector3 &locationDirectionVector = Vector3::NEGATIVE_UNIT_Z )
	{
		lookAt( targetPoint, relativeTo, locationDirectionVector );
	}

	//roll facade method
	void roll( const Radian &angle, 
		enum Ogre::Node::TransformSpace relativeTo = Ogre::Node::TS_LOCAL )
	{
		node->roll( angle, relativeTo );
	}

	//rotate facade method
	void rotate( const Vector3 &axis, const Radian &angle, 
		enum Ogre::Node::TransformSpace relativeTo = Ogre::Node::TS_LOCAL )
	{
		node->rotate( axis, angle, relativeTo );
	}

	//yaw facade method
	void yaw( const Ogre::Radian& degrees, Ogre::Node::TransformSpace relativeTo = Ogre::Node::TS_LOCAL )
	{
		node->yaw( degrees, relativeTo );
	}

	//setVisible facade method
	void setVisible( bool visible, bool cascade = true )
	{
		node->setVisible( visible, cascade );
	}

	void setRandPos()
	{
		//seed random number generator
		srand( time(0));
		
		Vector3 pos = this->getPosition();

		//normally 1 + rand() % value
		int valueX = ( 1000 - rand() % 2000 ); 
		//use existing value, as simply wish to yaw the node
		int valueY = pos.y; 
		int valueZ = ( 1000 - rand() % 2000 );
		this->setPosition( valueX, valueY, valueZ );
	}

	void setRandPosAway ( Watercraft *vessel, int distanceThresh = 200 )
	{
		double distance = 0;

		//repeat while distance is too small
		while ( distance < distanceThresh )
		{
			this->setRandPos();
			
			//get distance, for condition test
			distance = this->getRange( vessel );
		}
		//far enough random position set
	}

	//function to calculate distance between node of calling object and node of target
	double getRange( Watercraft *vessel )
	{
		//vessel type, as then can reuse to allign ships to each other
		//as well as test range between sub and ship
		Vector3 pos1 = this->getPosition();
		Vector3 pos2 = vessel->getPosition();
		
		//euclidian distance, using x and z, 
		//as y is vertical in this coordinate system
		double range = sqrt( pow((pos1.x - pos2.x), 2) + pow((pos1.z - pos2.z), 2));
		
		//return the calculated range
		return range;
	}

protected:
	int maxSpeed;
	int typicalSpeed;
	int health;
	SceneNode* node;
	Entity* ent;

	//internal methods, only used by constructor:
	virtual void initialiseObject( String &entityName, String ptype, SceneManager *mSceneMgr )
	{
		//don't have watercraft object
		//replace code in derived classes
	}

}; //end base class Watercraft
	
class Sub : public Watercraft
{
public:
	Sub()
	{

	}

	~Sub()
	{

	}

double getPeriscopeAngle( double rangeValue, double heightValue )
{
	//local constant v, virtual image conversation value
	const float v = 1.15;
	//reverse ranging equation to obtain angle at periscope
	double angle = ( ( heightValue * v * 1000 ) / rangeValue );

	return angle;
}

private:
	int diveDeepDepth;
	int periscopeDepth;
	SceneNode* nodePeriscope;
	Entity* entPeriscope;

void initialiseObject( String &entityName, String ptype, SceneManager *mSceneMgr )
{
	/////////////////////Create Submarine
	ent = mSceneMgr->createEntity( entityName, ptype );
	node = mSceneMgr->getRootSceneNode()->createChildSceneNode(); //no name required
	node->attachObject( ent );
	////////////////////End creating Submarine

	////////////////////Manipulate Submarine visual properties
	//double the size of the sub, as model too small
	node->scale(2, 2, 2 );

	//lower position of submarine, so submerged
	node->setPosition( 0, -30, 0 ); //x, y, z

	//yaw (rotate about x axis) the sub
	node->yaw( Degree(-90));
	////////////////////End Manipulating Submarine visual properties

	/////////////////////Create Periscope as child of sub
	entPeriscope = mSceneMgr->createEntity( "Periscope", "attack_periscope.mesh" );
	//make periscope child of submarine (also means no need to reposition, 
	//as position is relative). Also no need to scale
	nodePeriscope = node->createChildSceneNode();
	nodePeriscope->attachObject( entPeriscope );

	//create camera node - child of periscope
	//put camera in camera node
	//create 2nd camera node
}
}; //end derived class sub

class Ship : public Watercraft
{
public:
	Ship()
	{
	}

	~Ship()
	{

	}

private:

void initialiseObject( String &entityName, String ptype, SceneManager *mSceneMgr )
{
	/////////////////////Create Ship
	ent = mSceneMgr->createEntity( entityName, ptype );
	node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
	node->attachObject( ent );
	////////////////////End creating Ship

	//place the ship in a random position (range -1000 to 1000 )
	this->setRandPosAway( submarine, 200 );
}
}; //end derived class Ship

//Core class of the trainer
class Simulator
{
public:
    /// Standard constructor
    Simulator()
    {
        mFrameListener = 0;
        mRoot = 0;
    }
    /// Standard destructor
    ~Simulator()
    {
        if (mFrameListener)
            delete mFrameListener;
        if (mRoot)
            delete mRoot;
    }

    /// Start the example
    void go(void)
    {
        if (!setup())
            return;

        mRoot->startRendering();

        // clean up
        destroyScene();
    }

protected:
    Root *mRoot;
    Camera* mCamera;
    SceneManager* mSceneMgr;
    ExampleFrameListener* mFrameListener;
    RenderWindow* mWindow;

    // These internal methods package up the stages in the startup process
    // Sets up the application - returns false if the user chooses to 
	//abandon configuration.
    bool setup(void)
    {
        mRoot = new Root();

        setupResources();

        bool carryOn = configure();
        if (!carryOn) return false;

        chooseSceneManager();
        createCamera();
        createViewports();

        // Set default mipmap level (NB some APIs ignore this)
        TextureManager::getSingleton().setDefaultNumMipmaps(5);

		// Create any resource listeners (for loading screens)
		createResourceListener();
		// Load resources
		loadResources();

		// Create the scene
        createScene();

        createFrameListener();

        return true;

    }
    // Configures the application - returns false if the user chooses to 
	//abandon configuration.
    bool configure(void)
    {
        //Show the configuration dialog and initialise the system
        //You can skip this and use root.restoreConfig() to load configuration
        //settings if you were sure there are valid ones saved in ogre.cfg
        
		if(mRoot->showConfigDialog())
        {
            //If returned true, user clicked OK so initialise
            //Here we choose to let the system create a default 
			//rendering window by passing 'true'
            mWindow = mRoot->initialise(true, "Periscope Ranging Simulator");
            return true;
        }
        else
        {
            return false;
        }
    }

    void chooseSceneManager(void)
    {
        // Create the SceneManager exterior far
		// See: http://www.ogre3d.org/wiki/index.php/SceneManagersFAQ
        mSceneMgr = mRoot->createSceneManager( ST_EXTERIOR_FAR );
    }

	void createCamera(void)
    {
        // Create the camera
        mCamera = mSceneMgr->createCamera("PlayerCam");

        // Position it at scene origin
        mCamera->setPosition(Vector3(0,0,0)); //x,y,z
        // Look back along -Z
        //mCamera->lookAt(Vector3(0,0,-300));
        mCamera->setNearClipDistance(5);
    }

    void createFrameListener(void)
    {
        mFrameListener= new ExampleFrameListener(mWindow, mCamera);
        mFrameListener->showDebugOverlay(false);
        mRoot->addFrameListener(mFrameListener);
    }

    void createScene(void)
    {
		/////////////////////////Create Lights
		Light *light;
		mSceneMgr->setAmbientLight( ColourValue( .2, .2, .2)); //everything is slightly illuminated
		//create basic point light, no direction required. - so can see objects
		light = mSceneMgr->createLight( "Light1" );
		light->setType( Light::LT_POINT );
		light->setPosition( Vector3(0, 150, 250) );
		////////////////////////End creating lights
		
		//Create Skybox
		mSceneMgr->setSkyBox( true, "Examples/MorningSkyBox" );

		/////////////////////////Create ocean
		Ogre::Plane oceanSurface; 
		oceanSurface.normal = Ogre::Vector3::UNIT_Y; 
		oceanSurface.d = 20; 
		
		Ogre::MeshManager::getSingleton().createPlane("OceanSurface", 
			Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, 
			oceanSurface, 
			10000, 10000, 50, 50, true, 1, 1, 1, Ogre::Vector3::UNIT_Z); 
		
		Entity* mOceanSurfaceEnt = mSceneMgr->createEntity( "OceanSurface", "OceanSurface" ); 
		mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(mOceanSurfaceEnt); 
		mOceanSurfaceEnt->setMaterialName("Ocean2_HLSL_GLSL");
		////////////////////////End making ocean

		////////////////////////Create sub and ships, and setup positions
		Sub submarine( "Sub", "submarine5.mesh", mSceneMgr, 100, 35, 20 );

		Ship ship1( "Ship", "battle_ship2.mesh", mSceneMgr, 100, 45, 30 );
		//set position of ship
		ship1.setPosition( 0, -30, -1000 );
		
		//pass the scene manager as a parameter to the submarine's constructor, 
		//and create the scene node and entity yourself
		//-destroy both in destructor
		//create 'facade' methods, methods in submarine that call 
		//the methodso of scenenode that are required
    }

    void destroyScene(void){}    // Optional to override this

    void createViewports(void)
    {
        // Create one viewport, entire window
        Viewport* vp = mWindow->addViewport(mCamera);
        vp->setBackgroundColour(ColourValue(0,0,0));

        // Alter the camera aspect ratio to match the viewport
        mCamera->setAspectRatio(
            Real(vp->getActualWidth()) / Real(vp->getActualHeight()));
    }

    /// Method which will define the source of resources (other than current folder)
    void setupResources(void)
    {
        // Load resource paths from config file
        ConfigFile cf;
        cf.load("resources.cfg");

        // Go through all sections & settings in the file
        ConfigFile::SectionIterator seci = cf.getSectionIterator();

        String secName, typeName, archName;
        while (seci.hasMoreElements())
        {
            secName = seci.peekNextKey();
            ConfigFile::SettingsMultiMap *settings = seci.getNext();
            ConfigFile::SettingsMultiMap::iterator i;
            for (i = settings->begin(); i != settings->end(); ++i)
            {
                typeName = i->first;
                archName = i->second;
                ResourceGroupManager::getSingleton().addResourceLocation(
                    archName, typeName, secName);
            }
        }
    }

	/// Optional override method where you can create resource listeners (e.g. for loading screens)
	void createResourceListener(void)
	{

	}

	/// Optional override method where you can perform resource group loading
	/// Must at least do ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
	void loadResources(void)
	{
		// Initialise, parse scripts etc
		ResourceGroupManager::getSingleton().initialiseAllResourceGroups();

	}

};


#endif //1

#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
int main(int argc, char **argv)
#endif
{
    // Create application object
    Simulator app;

    try {
        app.go();
    } catch( Exception& e ) {
#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
        MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
        fprintf(stderr, "An exception has occured: %s\n",
                e.getFullDescription().c_str());
#endif
    }

    return 0;
}
The problem area is:

Code: Select all

class Ship : public Watercraft
{
public:
	Ship()
	{
	}

	~Ship()
	{

	}

private:
I don't need to itialise anything different from what is initialised in the Watercraft base class, so thought I could leave it blank?
But I get the error that there is no suitable default constructor.
Putting all the same parameters in as the Watercraft constructor causes the same error.

Sorry, this is probably rather noobish.
If you can help that'd be great!
Thanks!
Protagonist
Goblin
Posts: 214
Joined: Tue Nov 21, 2006 11:11 pm

Post by Protagonist »

I think I just got my constructor wrong. Some inheritance problem maybe :( . Hmm... if ayone could help that would be so great.
Thanks.
User avatar
Kencho
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4011
Joined: Fri Sep 19, 2003 6:28 pm
Location: Burgos, Spain
x 2

Post by Kencho »

Hmmm, I think I found the problem. When you define a constructor, default constructors aren't added. So the base class doesn't have a default constructor. Okay with this? Fine :) So when you define a derived class, you need to use the same constructor as the base class. And not only that: You should define your constructor in the form:

Code: Select all

MyConstructor (params) : MyBaseClassConstructor (params) {
...
}
I won't bet about this as my head is a mix of P-Codes, Java, C++ and Bison at this moment :? But that's probably the error.
Image
Protagonist
Goblin
Posts: 214
Joined: Tue Nov 21, 2006 11:11 pm

Post by Protagonist »

Thanks, that makes sense. I'll try it out.
Cheers :)