Rotate Sprite3D by World Axis, not Local Axis

Cocos2d-x
Is there any way to rotate a Sprite3D object by World Axis?

I am trying to do this:

ThreeDScene.cpp

#include "ThreeDScene.h"
#include "ui/CocosGUI.h"
#include "HelloWorldScene.h"

#include <iostream>

USING_NS_CC;

Scene* ThreeD::createScene()
{
    auto scene = ThreeD::create();
    return scene;
}

// Print useful error message instead of segfaulting when files are not there.
static void problemLoading(const char* filename)
{
    printf("Error while loading: %s\n", filename);
    printf("Depending on how you compiled you might have to add 'Resources/' in front of filenames in HelloWorldScene.cpp\n");
}

// on "init" you need to initialize your instance
bool ThreeD::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Scene::init() )
    {
        return false;
    }

    auto visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

    /////////////////////////////
    // 3. add your codes below...
    
    // button TODO
    auto nextButton = ui::Button::create("sprites/rick.png");
    nextButton->setScale(0.2);
    nextButton->setPosition(Vec2(100, 100));
    nextButton->addTouchEventListener([&](Ref* sender, ui::Widget::TouchEventType type){
        switch (type)
        {
            case ui::Widget::TouchEventType::BEGAN:
                break;
            case ui::Widget::TouchEventType::ENDED:
                Director::getInstance()->replaceScene(HelloWorld::create());
                break;
            default:
                break;
        }
    });
    this->addChild(nextButton, 1);
    
    
    // base 3d cubie
    // blender default cube, no adjustments
    auto baseCubie = Sprite3D::create("sprites/cubie.obj");
    baseCubie->setScale(12);
    this->addChild(baseCubie);
    
    // using western color scheme
    numberByNumber = 3;
    int distance = 2;
    float toCenterOffset = (numberByNumber-1)/2*distance;
    
    // make 3d array
    // int cubeArray[numberByNumber][numberByNumber][numberByNumber];
    // make 3d cube
    Vector<Sprite3D*> clonedCubiesVector;
    for (int i = 0; i < numberByNumber; i++)
    {
        for (int j = 0; j < numberByNumber; j++)
        {
            for (int k = 0; k < numberByNumber; k++)
            {
                // clonedCubie is a kinda switching variable
                auto clonedCubie = Sprite3D::create("sprites/cubie.obj");
                clonedCubie->setTexture("textures/rubikscube.png");
                // do not forget the - in front of Vec3
                clonedCubie->setPosition3D(-Vec3(toCenterOffset, toCenterOffset, toCenterOffset) + baseCubie->getPosition3D() + Vec3(i*distance, j*distance, k*distance));
                //baseCubie->addChild(clonedCubie); // use for things
                clonedCubiesVector.pushBack(clonedCubie);
            }
        }
    }
    // addChild all items in vector
    
    for (int i = 0; i < clonedCubiesVector.size(); i++)
    {
        baseCubie->addChild(clonedCubiesVector.at(i));
    }
    baseCubie->setPosition3D(Vec3(175, 350, 300));
    float controlSpeed = 80;
    auto listener1 = EventListenerTouchAllAtOnce::create();

    listener1->onTouchesBegan = [=](const std::vector<cocos2d::Touch*>& touches, cocos2d::Event* event){
        if (touches.size() == 1)
        {
        }
    };
    
    listener1->onTouchesMoved = [=](const std::vector<cocos2d::Touch*>& touches, cocos2d::Event* event){
        if (touches.size() == 1)
        {
            float deltaTime = Director::getInstance()->getDeltaTime();
            
            auto touch = touches[0];
            Vec2 tLocation = touch->getLocation(); // t for touch
            Vec2 tPreviousLocation = touch->getPreviousLocation();
            Vec2 changePos = (tPreviousLocation - tLocation)*deltaTime*controlSpeed; // can use CC_DEGREES_TO_RADIANS
            // TODO rotation
            Vec3 oRotation = baseCubie->getRotation3D(); // o for origin
            
            Vec3 baseSet = Vec3(changePos.y, -changePos.x, 0);
            Vec3 result = Vec3(baseSet+oRotation);
            baseCubie->setRotation3D(result);
            log("%f, %f, %f", oRotation.x, oRotation.y, baseSet.z);
        }
    };
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, this);
    
    return true;
}

ThreeDScene.h

#ifndef __THREED_SCENE_H__
#define __THREED_SCENE_H__

#include "cocos2d.h"

class ThreeD : public cocos2d::Scene
{
public:
    static cocos2d::Scene* createScene();

    virtual bool init();
    
    // a selector callback
    void menuCloseCallback(cocos2d::Ref* pSender);
    
    // implement the "static create()" method manually
    CREATE_FUNC(ThreeD);
    
    
    int numberByNumber;
    enum Colors
    {
                WHITE,
        ORANGE, GREEN, RED, BLUE,
                YELLOW
    };
};

#endif // __THREED_SCENE_H__

I wasted almost a week for this :frowning:

Have a look at nodeToWorldTransform() or getNodeToWorldTransform().
Those are 4x4 Matrices converting from node to world coordinate system.

Without testing I’m not quiet sure how to apply them, probably multiply your result Rotation with the Matrix to convert it from Local to World Coordinate System and only then call setRotation3D.
For that you need to extend your Rotation to 4D. Just add 1 in the 4th dimension if you don’t want to have any perspective shenanigans.

Let me know if it worked. I’m sincerely interested since I haven’t worked in 3D yet.

1 Like

@IronBytes I finally managed to get this working.

Thanks for your help :slight_smile:

I was trying to do this: http://math.hws.edu/eck/cs424/notes2013/webgl/cube-with-rotator.html

I used Quaternions to rotate the cube.

qRotation *= Quaternion(baseCubie->getWorldToNodeTransform() * Vec3::UNIT_X, CC_DEGREES_TO_RADIANS(angles.x * dt));
qRotation *= Quaternion(baseCubie->getWorldToNodeTransform() * Vec3::UNIT_Y, CC_DEGREES_TO_RADIANS(angles.y * dt));
qRotation *= Quaternion(baseCubie->getWorldToNodeTransform() * Vec3::UNIT_Z, CC_DEGREES_TO_RADIANS(angles.z * dt));

I don’t know how to explain this, but this topic helped: Animation of rotation around axis using quaternions

Here is the source code:
ThreeDScene.cpp

#include "ThreeDScene.h"
#include "HelloWorldScene.h"
#include <ui/CocosGUI.h>


#include <iostream>

USING_NS_CC;

Scene* ThreeD::createScene()
{
    auto scene = ThreeD::create();
    return scene;
}

// Print useful error message instead of segfaulting when files are not there.
static void problemLoading(const char* filename)
{
    printf("Error while loading: %s\n", filename);
    printf("Depending on how you compiled you might have to add 'Resources/' in front of filenames in HelloWorldScene.cpp\n");
}

// on "init" you need to initialize your instance
bool ThreeD::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Scene::init() )
    {
        return false;
    }

    auto visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

    /////////////////////////////
    // 3. add your codes below...
    
    // TODO: button
    auto nextButton = ui::Button::create("sprites/rick.png");
    nextButton->setScale(0.2);
    nextButton->setPosition(Vec2(100, 100));
    nextButton->addTouchEventListener([&](Ref* sender, ui::Widget::TouchEventType type){
        switch (type)
        {
            case ui::Widget::TouchEventType::BEGAN:
                break;
            case ui::Widget::TouchEventType::ENDED:
                Director::getInstance()->replaceScene(HelloWorld::create());
                break;
            default:
                break;
        }
    });
    this->addChild(nextButton, 1);
    
    
    // base 3d cubie
    // blender default cube, no adjustments
    auto baseCubie = Sprite3D::create("sprites/cubie.obj");
    baseCubie->setScale(12);
    
    this->addChild(baseCubie);
    
    // using western color scheme
    numberByNumber = 3; // 3 idk
    int distance = 2; // 2 prob
    float toCenterOffset = (numberByNumber-1)/2*distance;
    
    // make 3d array
    // int cubeArray[numberByNumber][numberByNumber][numberByNumber];
    // make 3d cube
    Vector<Sprite3D*> clonedCubiesVector;
    for (int i = 0; i < numberByNumber; i++)
    {
        for (int j = 0; j < numberByNumber; j++)
        {
            for (int k = 0; k < numberByNumber; k++)
            {
                // clonedCubie is a kinda switching variable
                auto clonedCubie = Sprite3D::create("sprites/cubie.obj");
                clonedCubie->setTexture("sprites/cubietexture.png");
                // do not forget the - in front of Vec3
                clonedCubie->setPosition3D(-Vec3(toCenterOffset, toCenterOffset, toCenterOffset) + baseCubie->getPosition3D() + Vec3(i*distance, j*distance, k*distance));
                //baseCubie->addChild(clonedCubie); // use for things
                clonedCubiesVector.pushBack(clonedCubie);
            }
        }
    }
    // addChild all items in vector
    
    for (int i = 0; i < clonedCubiesVector.size(); i++)
    {
        baseCubie->addChild(clonedCubiesVector.at(i));
    }
    baseCubie->setPosition3D(Vec3(175, 350, 300));
    float controlSpeed = 40;
    auto listener1 = EventListenerTouchAllAtOnce::create();

    listener1->onTouchesBegan = [=](const std::vector<cocos2d::Touch*>& touches, cocos2d::Event* event){
        if (touches.size() == 1)
        {
        }
    };
    
    listener1->onTouchesMoved = [=](const std::vector<cocos2d::Touch*>& touches, cocos2d::Event* event){
        if (touches.size() == 1)
        {
            float dt = Director::getInstance()->getDeltaTime();
            
            auto touch = touches[0];
            Vec2 tLocation = touch->getLocation(); // t for touch
            Vec2 tPreviousLocation = touch->getPreviousLocation();
            Vec2 changePos = (tLocation-tPreviousLocation)*controlSpeed;
            
    
            // TODO: rotation
            // this part is IMPORTANT, I wasted weeks because of this
            Vec3 angles = Vec3(-changePos.y, changePos.x, 0);
            Quaternion qRotation = baseCubie->getRotationQuat();
            qRotation *= Quaternion(baseCubie->getWorldToNodeTransform() * Vec3::UNIT_X, CC_DEGREES_TO_RADIANS(angles.x * dt));
            qRotation *= Quaternion(baseCubie->getWorldToNodeTransform() * Vec3::UNIT_Y, CC_DEGREES_TO_RADIANS(angles.y * dt));
            qRotation *= Quaternion(baseCubie->getWorldToNodeTransform() * Vec3::UNIT_Z, CC_DEGREES_TO_RADIANS(angles.z * dt));
            baseCubie->setRotationQuat(qRotation);
            
            
            /*
            Quaternion q = baseCubie->getRotationQuat();
            q *= Quaternion(baseCubie->getWorldToNodeTransform() * (horizontal ? Vec3::UNIT_Y : Vec3::UNIT_X), controlSpeed*deltaTime);
            baseCubie->setRotationQuat(q);
             */
            /*
             Vec3 oRotation = baseCubie->getRotation3D(); // o for origin
             Vec3 baseSet = Vec3(changePos.y, -changePos.x, 0);
             Vec3 result = Vec3(baseSet+oRotation);
             baseCubie->setRotation3D(result);
             log("%f, %f, %f", oRotation.x, oRotation.y, baseSet.z);

             Quaternion originalPos = baseCubie->getRotationQuat();
             Quaternion addPos = Quaternion(0.5, 0.5, 0, 0);
             addPos.multiply(originalPos);
             addPos.normalize();
             baseCubie->setRotationQuat(addPos);
             */
        }
    };
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, this);
    
    return true;
}

ThreeDScene.h

// HelloWorldScene.cpp, HelloWorldScene.h will probably be the timer thing

#ifndef __THREED_SCENE_H__
#define __THREED_SCENE_H__

#include "cocos2d.h"

class ThreeD : public cocos2d::Scene
{
public:
    static cocos2d::Scene* createScene();

    virtual bool init();
    
    // a selector callback
    void menuCloseCallback(cocos2d::Ref* pSender);
    
    // implement the "static create()" method manually
    CREATE_FUNC(ThreeD);
    
    
    int numberByNumber;
    enum Colors
    {
                WHITE,
        ORANGE, GREEN, RED, BLUE,
                YELLOW
    };
    
};

#endif // __THREED_SCENE_H__

Nice, I’m glad I could point you into the right direction. Thanks taking the time to post your solution :+1:

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.