[SOLVED] Saving SceneNode's Rotation to Data

Anything and everything that's related to OGRE or the wider graphics field that doesn't fit into the other forums.
Post Reply
User avatar
cool_thomas
Kobold
Posts: 29
Joined: Wed Oct 09, 2013 3:41 pm

[SOLVED] Saving SceneNode's Rotation to Data

Post by cool_thomas »

So I have some world data for buildings and what not that looks like this:

Code: Select all

<group name="north_castle_area">
            <rotate yaw="90" />
            <static name="fortifiedmanor" group="OldTest">
                    <mesh file="fortifiedmanor.mesh" />
                    <setting ysnap="relative" />
                    <position x="7500" y="-612" z="-500" />
                    <scale x="2.8" y="2.8" z="2.8" />
                    <rotate pitch="0" yaw="180" />
            </static>
</group>
I then have a save function for the data that looks like this:

Code: Select all

    void Object::saveObject(tinyxml2::XMLDocument *xmlDoc, tinyxml2::XMLElement *elt)
    {
        tinyxml2::XMLElement *objElt = xmlDoc->NewElement("static");
        elt->InsertEndChild(objElt);
        objElt->SetAttribute("name", ObjectName.c_str());
        objElt->SetAttribute("group", DefaultGroup.c_str());

        const Ogre::SceneNode::ObjectIterator it = ObjectNode->getAttachedObjectIterator();
        while(it.hasMoreElements())
        {
            tinyxml2::XMLElement *meshElt = xmlDoc->NewElement("mesh");
            objElt->InsertEndChild(meshElt);
            Ogre::Entity *entity = static_cast<Entity*>(it.getNext());
            meshElt->SetAttribute("file", entity->getMesh()->getName().c_str());
            meshElt->SetAttribute("group", entity->getMesh()->getGroup().c_str());
        }

        tinyxml2::XMLElement *posElt = xmlDoc->NewElement("position");
        Ogre::Vector3 pos = ObjectNode->_getDerivedPosition();
        posElt->SetAttribute("x", pos.x);
        posElt->SetAttribute("y", pos.y);
        posElt->SetAttribute("z", pos.z);
        objElt->InsertEndChild(posElt);

        tinyxml2::XMLElement *rotElt = xmlDoc->NewElement("rotate");
        Ogre::Quaternion quat = ObjectNode->getOrientation();
        rotElt->SetAttribute("roll", quat.getRoll().valueDegrees());
        rotElt->SetAttribute("pitch", quat.getPitch().valueDegrees());
        rotElt->SetAttribute("yaw", quat.getYaw().valueDegrees());
        objElt->InsertEndChild(rotElt);

        tinyxml2::XMLElement *scaleElt = xmlDoc->NewElement("scale");
        Ogre::Vector3 scale = ObjectNode->_getDerivedScale();
        scaleElt->SetAttribute("x", scale.x);
        scaleElt->SetAttribute("y", scale.y);
        scaleElt->SetAttribute("z", scale.z);
        objElt->InsertEndChild(scaleElt);
    }
Everything is working fine except for the rotation which results in this:

Code: Select all

   <static name="fortifiedmanor" group="OldTest">
        <mesh file="fortifiedmanor.mesh" group="OldTest"/>
        <position x="-5.00002" y="198.586" z="-75"/>
        <rotate roll="180" pitch="0" yaw="-180"/>
        <scale x="0.028" y="0.028" z="0.028"/>
    </static>
The final result should be:

Code: Select all

<static name="fortifiedmanor" group="OldTest">
        <mesh file="fortifiedmanor.mesh" group="OldTest"/>
        <position x="-5.00002" y="198.586" z="-75"/>
        <rotate roll="0" pitch="0" yaw="-90"/>
        <scale x="0.028" y="0.028" z="0.028"/>
    </static>
Basically, I want the end result to be the exact result independent of the group modifications (which puts the objects node inside another scenenode and rotates/scales/moves based on that).

Anyway, the object is getting turned upside down.

I have tried different approaches such as using _getDerivedOrientation; as well as adding that or multiplying that with getOrientation.

Thanks.
Last edited by cool_thomas on Sat Apr 12, 2014 1:38 am, edited 1 time in total.
loath
Platinum Sponsor
Platinum Sponsor
Posts: 290
Joined: Tue Jan 17, 2012 5:18 am
x 67

Re: Saving SceneNode's Rotation to Data

Post by loath »

i have my own file format, but i save rotations as quaternions:

Code: Select all

 <group paging="">
		<prop type="shipwreck">
			<loc>
				<pos x="-19125" y="1841.01526" z="5175"/>
				<rot qw="0.707106769" qx="0" qy="-0.707106769" qz="0"/>
			</loc>
		</prop>
	</group>
and leave out rotation if it's the identity (i.e. as the model is in object space):

Code: Select all

 <group paging="">
		<prop type="shipwreck">
			<loc>
				<pos x="12175" y="1832.396" z="2575"/>
			</loc>
		</prop>
	</group>
User avatar
Kojack
OGRE Moderator
OGRE Moderator
Posts: 7157
Joined: Sun Jan 25, 2004 7:35 am
Location: Brisbane, Australia
x 534

Re: Saving SceneNode's Rotation to Data

Post by Kojack »

What are the values of the quaternion that you are using to get the euler angles? (ObjectNode->getOrientation())
Is ObjectNode a child of other nodes? In your first example there's a 90 degree yaw outside of the static tag, that makes it sound like maybe the group has a node with a rotation then the statics inside have nodes with rotations. Or do the outer and inner rotations just combine together?
What order of euler angles are you using to load those rotation values? The order of pitch, roll and yaw will change the end result. To use ogre's getPitch and getYaw you need to have built the quaternion with the same order as ogre expects (there's 6 combinations).
For my own euler angle stuff (the euler class in the wiki) I ignore the quaternion getPitch, getYaw and getRoll, since they use the wrong order for my class, and use the Matrix3 class methods to get the angles (there's six of them, one for each order).
User avatar
cool_thomas
Kobold
Posts: 29
Joined: Wed Oct 09, 2013 3:41 pm

Re: Saving SceneNode's Rotation to Data

Post by cool_thomas »

I think saving the quaternions instead and using that would probably be best, since it would have better accuracy.

As far as the order of rotations; yes a heirarchy of nodes is created that goes from outermost group to innermost group then statics.

This works as follows:
Groups are loaded and have SceneNode's created, then Objects are created with their SceneNodes with its parent set to the group's SceneNode. The object's position and rotation are applied first however, followed by the groups position.

Here is the code for it:

Code: Select all

void World::loadObjects(tinyxml2::XMLDocument *worldDoc)
    {
        LOG("World: Parsing objects");
        tinyxml2::XMLElement *element = worldDoc->FirstChildElement("objects");
        ObjectFactory objFactory = ObjectFactory();
        if(element)
        {
            LOG("Loading Independent Objects");
            loadObjectGroup(element, &objFactory);

            LOG("Loading Groups");

            processGroup(element, &objFactory);
        }

        for(unsigned int i = 0; i < ObjectData.size(); i++)
        {
            ObjectData[i]->resetY();
        }
    }

    void World::processGroup(tinyxml2::XMLElement *groupElt, ObjectFactory *objFactory, Ogre::SceneNode *grpNode)
    {
        tinyxml2::XMLElement *group = groupElt->FirstChildElement("group");
        static int grpCount = 0;
        while(group)
        {
            const char* grpn = group->Attribute("name");
            Ogre::String grpName = "GRP";
            if(grpn != NULL)
            {
                grpName += Ogre::String(grpn + Ogre::StringConverter::toString(grpCount));
            }
            else
            {
                grpName += "group" + Ogre::StringConverter::toString(grpCount);
            }
            if( group->BoolAttribute("disabled") )
            {
                LOG("Group " + grpName + " disabled");
                group = group->NextSiblingElement("group");
                continue;
            }
            LOG("Loading group: " + grpName);

            Ogre::SceneNode *parent;
            if(grpNode)
            {
                parent = grpNode;
            }
            else
            {
                parent = objectsNode;
            }
            Ogre::SceneNode *groupNode = parent->createChildSceneNode(grpName);
            processGroup(group, objFactory, groupNode);
            loadObjectGroup(group, objFactory, groupNode);

            float x, y, z;

            if(loadPosition(group, x, y, z))
            {
                groupNode->setPosition(Ogre::Vector3(x, y, z));
            }

            float sx, sy, sz;
            if(loadScale(group, sx, sy, sz))
            {
                groupNode->setScale(sx, sy, sz);
            }
            float roll, pitch, yaw;
            roll = pitch = yaw = 0;
            if(loadRotate(group, roll, pitch, yaw))
            {
                groupNode->roll(Ogre::Radian(Ogre::Math::DegreesToRadians(roll)));
                groupNode->pitch(Ogre::Radian(Ogre::Math::DegreesToRadians(pitch)));
                groupNode->yaw(Ogre::Radian(Ogre::Math::DegreesToRadians(yaw)));
            }

            group = group->NextSiblingElement("group");
        }
    }

    void World::loadObjectGroup(tinyxml2::XMLElement *objectElt, ObjectFactory *objFactory, Ogre::SceneNode *grpNode)
    {
        tinyxml2::XMLElement *object = objectElt->FirstChildElement("static");
        Ogre::SceneNode *parent;
        if(grpNode)
        {
            parent = grpNode;
        }
        else
        {
            parent = objectsNode;
        }
        while(object)
        {
            LOG("Creating Objects: " + Ogre::String(object->Attribute("name")));
            ObjectData.push_back(objFactory->createObject(object, parent));

            object = object->NextSiblingElement("static");
        }
    }
And the object code:

Code: Select all

bool Object::init(tinyxml2::XMLElement *objElt, Ogre::SceneNode *parentNode)
    {
        WorldPtr = APP->getWorldPtr();
        const char* objName = objElt->Attribute("name");
        if(objName != NULL)
        {
            ObjectName = std::string(objName);
            createNode(objName, parentNode);
        }
        else
        {
            createNode(parentNode);
        }

        const char* grp = objElt->Attribute("group");
        if(grp != NULL)
        {
            DefaultGroup = std::string(grp);
        }
        else
        {
            DefaultGroup = "General";
        }

        tinyxml2::XMLElement *meshElt = objElt->FirstChildElement("mesh");

        while(meshElt != 0)
        {
            Ogre::String meshFile;
            meshFile = Ogre::String(meshElt->Attribute("file"));
            const char* entGroup = meshElt->Attribute("group");
            Ogre::String meshGroup;
            if(entGroup != NULL)
            {
                meshGroup = Ogre::String(entGroup);
            }
            else
            {
                meshGroup = DefaultGroup;
            }
            LOG("Loading mesh: " + meshFile);

            addEntity(meshFile, meshGroup);

            meshElt = meshElt->NextSiblingElement("mesh");
        }
        tinyxml2::XMLElement *settingElt = objElt->FirstChildElement("setting");
        if(settingElt)
        {
            const char *ypos = settingElt->Attribute("ysnap");
            if(ypos != NULL)
            {
                if(strcmp(ypos, "relative") == 0)
                {
                    setYsnap(Y_RELATIVE);
                }
                else if(strcmp(ypos, "absolute") == 0)
                {
                    setYsnap(Y_ABSOLUTE);
                }
                else
                {
                    setYsnap(Y_TERRAIN);
                }
            }
        }
User avatar
cool_thomas
Kobold
Posts: 29
Joined: Wed Oct 09, 2013 3:41 pm

Re: Saving SceneNode's Rotation to Data

Post by cool_thomas »

So I modified it to just save the quaternion rotation and works perfectly.
Post Reply