Cocos2d-x 3.3 的3D开发功能介绍

昨天去成都参加GMGDC 全球移动游戏开发者大会,据蓝港互动CEO王峰谈到《手机游戏开发如何走好第一步》时谈到

目前手游公司有10000家,没错,红海,都快变黑海了

这么多公司和产品如何竞争,大量的同质化产品,(王峰给我的建议是与其在红海中厮杀不如想象未来的蓝海在哪,听明白的请举起右手)

先不管这些了,作为程序员来讲我们看到了20万的人才缺口(如果每家公司招20个人,听明白的请举起左手)

今天下载了Cocos2d-x 3.3,3D功能果然强大

主要有以下功能:

1. 基本的Sprite3D使用,加载静态模型和动态模型,看 Sprite3DBasicTest

2.Sprite3D对象的旋转,缩放等Action操作

3.Sprite3D中使用Shader特效,实现outLine

4.Animate3D来创建3D动画

5.动态增加3D骨骼,实现怪物添加手持武器功能

6,动态修改骨骼皮肤实现换装功能Sprite3DReskinTest

7.通过包围盒实现3D模型碰撞,Sprite3DWithOBBPerfromanceTest

8.实现水平镜像3D模型,Sprite3DMirrorTest


下面介绍一下Sprite3DTest里面的源码



#include "Sprite3DTest.h"

#include "3d/CCAnimation3D.h"

#include "3d/CCAnimate3D.h"

#include "3d/CCAttachNode.h"

#include "3d/CCRay.h"

#include "3d/CCSprite3D.h"

#include "renderer/CCVertexIndexBuffer.h"

#include "DrawNode3D.h"


1.在Scene中添加3D模型

Cocos2d-x 3.3 的3D开发功能介绍_第1张图片

void Sprite3DBasicTest::addNewSpriteWithCoords(Vec2 p)

{

//这里的obj可以使用3dmax直接导出

//    //option 1: load a obj that contain the texture in it  第一种方法是在模型文件中包含了纹理

//    auto sprite = Sprite3D::create("sprite3dTest/scene01.obj");    

    //option 2: load obj and assign the texture   第二种方法是在模型文件中不包含纹理

    auto sprite =Sprite3D::create("Sprite3DTest/boss1.obj");

    sprite->setScale(3.f);

    sprite->setTexture("Sprite3DTest/boss.png");

    //在Sprite3D中包含了一些基本的Shader特效,下面是让3D模型实现outline

    //sprite->setEffect(cocos2d::EFFECT_OUTLINE);    

    //add to scene

    addChild( sprite );

    sprite->setPosition( Vec2( p.x, p.y) );

    ActionInterval* action;

    float random = CCRANDOM_0_1();

    

    if( random < 0.20 )

        action = ScaleBy::create(3,2);

    else if(random <0.40)

        action = RotateBy::create(3,360);

    else if( random <0.60)

        action = Blink::create(1,3);

    else if( random <0.8 )

        action = TintBy::create(2,0, -255, -255);

    else

        action = FadeOut::create(2);

    auto action_back = action->reverse();

    auto seq = Sequence::create( action, action_back,nullptr );   

    sprite->runAction( RepeatForever::create(seq) );

//以上大家看到Sprite3D起始和Sprite类似都可以实现各种Action

}

void Sprite3DBasicTest::onTouchesEnded(conststd::vector<Touch*>& touches,Event* event)

{

    for (auto touch: touches)

    {

        auto location = touch->getLocation();

        //触摸屏幕添加3D模型

        addNewSpriteWithCoords( location );

    }

}

2.透过触摸屏幕拖动模型移动


Cocos2d-x 3.3 的3D开发功能介绍_第2张图片

Sprite3DHitTest::Sprite3DHitTest()

{

    auto s =Director::getInstance()->getWinSize();

    auto sprite1 =Sprite3D::create("Sprite3DTest/boss1.obj");

    

    sprite1->setScale(4.f);

    sprite1->setTexture("Sprite3DTest/boss.png");

    sprite1->setPosition( Vec2(s.width/2, s.height/2) );

    

    //add to scene

    addChild( sprite1 );

    sprite1->runAction(RepeatForever::create(RotateBy::create(3,360)));

    

    auto sprite2 =Sprite3D::create("Sprite3DTest/boss1.obj");

    

    sprite2->setScale(4.f);

    sprite2->setTexture("Sprite3DTest/boss.png");

    sprite2->setPosition( Vec2(s.width/2, s.height/2) );

    sprite2->setAnchorPoint(Vec2(0.5,0.5));

    

    //add to scene

    addChild( sprite2 );

    sprite2->runAction(RepeatForever::create(RotateBy::create(3, -360)));

    

    

    // Make sprite1 touchable

    auto listener1 =EventListenerTouchOneByOne::create();

    listener1->setSwallowTouches(true);

    

    listener1->onTouchBegan = [](Touch* touch,Event* event){

        auto target = static_cast<Sprite3D*>(event->getCurrentTarget());

      

        Rect rect = target->getBoundingBox();        

        if (rect.containsPoint(touch->getLocation()))

        {

            log("sprite3d began... x = %f, y = %f", touch->getLocation().x, touch->getLocation().y);

            target->setOpacity(100);

            return true;

        }

        return false;

    };

    

    listener1->onTouchMoved = [](Touch* touch,Event* event){

        auto target = static_cast<Sprite3D*>(event->getCurrentTarget());

        target->setPosition(target->getPosition() + touch->getDelta());

    };

    

    listener1->onTouchEnded = [=](Touch* touch,Event* event){

        auto target = static_cast<Sprite3D*>(event->getCurrentTarget());

        log("sprite3d onTouchesEnded.. ");

        target->setOpacity(255);

    };

    

    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, sprite1);

    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(), sprite2);

    

}

3.在一个Sprite3D上使用Shader


Effect3D继承REF封装了Shader的处理

Effect3DOutline继承了Effect3D,封装了Outline类型的Shader

EffectSprite3D继承了Sprite3D封装了对Effect3DOutline的调用

class Effect3D : publicRef

{

public:

    virtual void draw(constMat4 &transform) = 0;

    virtual void setTarget(EffectSprite3D *sprite) =0;

protected:

    Effect3D() : _glProgramState(nullptr) {}

    virtual ~Effect3D()

    {

        CC_SAFE_RELEASE(_glProgramState);

    }

protected:

    GLProgramState* _glProgramState;

};


class Effect3DOutline: publicEffect3D

{

public:

    static Effect3DOutline* create();

    

    void setOutlineColor(constVec3& color);

    

    void setOutlineWidth(float width);

    

    virtual void draw(constMat4 &transform) override;

    virtual void setTarget(EffectSprite3D *sprite) override;

protected:

    

    Effect3DOutline();

    virtual ~Effect3DOutline();

    

    bool init();

    

    Vec3 _outlineColor;

    float _outlineWidth;

    //weak reference

    EffectSprite3D* _sprite;

#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)

    EventListenerCustom* _backToForegroundListener;

#endif

    

protected:

    static const std::string _vertShaderFile;

    static const std::string _fragShaderFile;

    static const std::string _keyInGLProgramCache;

    

    static const std::string _vertSkinnedShaderFile;

    static const std::string _fragSkinnedShaderFile;

    static const std::string _keySkinnedInGLProgramCache;

    

    static GLProgram* getOrCreateProgram(bool isSkinned =false);

};

class EffectSprite3D : publicSprite3D

{

public:

    static EffectSprite3D* createFromObjFileAndTexture(conststd::string& objFilePath, const std::string& textureFilePath);

    static EffectSprite3D* create(conststd::string& path);

    

    void setEffect3D(Effect3D* effect);

    void addEffect(Effect3DOutline* effect,ssize_t order);

    virtual void draw(Renderer *renderer,const Mat4 &transform, uint32_t flags) override;

protected:

    EffectSprite3D();

    virtual ~EffectSprite3D();

    

    std::vector<std::tuple<ssize_t,Effect3D*,CustomCommand>> _effects;

    Effect3D* _defaultEffect;

    CustomCommand _command;

};


class Sprite3DEffectTest : public Sprite3DTestDemo

{

public:

    CREATE_FUNC(Sprite3DEffectTest);

    Sprite3DEffectTest();

    virtual std::string title()const override;

    virtual std::string subtitle()const override;

    

    void addNewSpriteWithCoords(Vec2 p);

    

    void onTouchesEnded(conststd::vector<Touch*>& touches,Event* event);

};


Cocos2d-x 3.3 的3D开发功能介绍_第3张图片


const std::stringEffect3DOutline::_vertShaderFile = "Shaders3D/OutLine.vert";

const std::stringEffect3DOutline::_fragShaderFile = "Shaders3D/OutLine.frag";

const std::stringEffect3DOutline::_keyInGLProgramCache = "Effect3DLibrary_Outline";


const std::stringEffect3DOutline::_vertSkinnedShaderFile = "Shaders3D/SkinnedOutline.vert";

const std::stringEffect3DOutline::_fragSkinnedShaderFile = "Shaders3D/OutLine.frag";

const std::stringEffect3DOutline::_keySkinnedInGLProgramCache = "Effect3DLibrary_Outline";

GLProgram* Effect3DOutline::getOrCreateProgram(bool isSkinned/* = false */ )

{

    if(isSkinned)

    {

        auto program = GLProgramCache::getInstance()->getGLProgram(_keySkinnedInGLProgramCache);

        if(program == nullptr)

        {

            program = GLProgram::createWithFilenames(_vertSkinnedShaderFile,_fragSkinnedShaderFile);

            GLProgramCache::getInstance()->addGLProgram(program,_keySkinnedInGLProgramCache);

        }

        return program;

    }

    else

    {

        auto program = GLProgramCache::getInstance()->getGLProgram(_keyInGLProgramCache);

        if(program == nullptr)

        {

            program = GLProgram::createWithFilenames(_vertShaderFile,_fragShaderFile);

            GLProgramCache::getInstance()->addGLProgram(program,_keyInGLProgramCache);

        }

        return program;

    }


}


Effect3DOutline* Effect3DOutline::create()

{

    Effect3DOutline* effect =new (std::nothrow)Effect3DOutline();

    if(effect && effect->init())

    {

        effect->autorelease();

        return effect;

    }

    else

    {

        CC_SAFE_DELETE(effect);

        return nullptr;

    }

}


bool Effect3DOutline::init()

{

    

    return true;

}


Effect3DOutline::Effect3DOutline()

: _outlineWidth(1.0f)

, _outlineColor(1,1, 1)

, _sprite(nullptr)

{

#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)

    _backToForegroundListener = EventListenerCustom::create(EVENT_RENDERER_RECREATED,

                                                          [this](EventCustom*)

                                                          {

                                                              auto glProgram = _glProgramState->getGLProgram();

                                                              glProgram->reset();

                                                              glProgram->initWithFilenames(_vertShaderFile, _fragShaderFile);

                                                              glProgram->link();

                                                              glProgram->updateUniforms();

                                                          }

                                                          );

    Director::getInstance()->getEventDispatcher()->addEventListenerWithFixedPriority(_backToForegroundListener, -1);

#endif

}


Effect3DOutline::~Effect3DOutline()

{

#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)

    Director::getInstance()->getEventDispatcher()->removeEventListener(_backToForegroundListener);

#endif

}


void Effect3DOutline::setOutlineColor(constVec3& color)

{

    if(_outlineColor != color)

    {

        _outlineColor = color;

        if(_glProgramState)

            _glProgramState->setUniformVec3("OutLineColor",_outlineColor);

    }

}


void Effect3DOutline::setOutlineWidth(float width)

{

    if(_outlineWidth != width)

    {

        _outlineWidth = width;

        if(_glProgramState)

            _glProgramState->setUniformFloat("OutlineWidth",_outlineWidth);

    }

}


void Effect3DOutline::setTarget(EffectSprite3D *sprite)

{

    CCASSERT(nullptr != sprite &&nullptr != sprite->getMesh(),"Error: Setting a null pointer or a null mesh EffectSprite3D to Effect3D");

    

    if(sprite != _sprite)

    {

        GLProgram* glprogram;

        if(!sprite->getMesh()->getSkin())

            glprogram = GLProgram::createWithFilenames(_vertShaderFile,_fragShaderFile);

        else

            glprogram = GLProgram::createWithFilenames(_vertSkinnedShaderFile,_fragSkinnedShaderFile);


        _glProgramState = GLProgramState::create(glprogram);


        _glProgramState->retain();

        _glProgramState->setUniformVec3("OutLineColor",_outlineColor);

        _glProgramState->setUniformFloat("OutlineWidth",_outlineWidth);

    

        

        _sprite = sprite;

        

        auto mesh = sprite->getMesh();

        long offset = 0;

        for (auto i =0; i < mesh->getMeshVertexAttribCount(); i++)

        {

            auto meshvertexattrib = mesh->getMeshVertexAttribute(i);

            

            _glProgramState->setVertexAttribPointer(s_attributeNames[meshvertexattrib.vertexAttrib],

                                                    meshvertexattrib.size,

                                                    meshvertexattrib.type,

                                                    GL_FALSE,

                                                    mesh->getVertexSizeInBytes(),

                                                    (void*)offset);

            offset += meshvertexattrib.attribSizeBytes;

        }

        

        Color4F color(_sprite->getDisplayedColor());

        color.a = _sprite->getDisplayedOpacity() /255.0f;

        _glProgramState->setUniformVec4("u_color",Vec4(color.r, color.g, color.b, color.a));

    }

    

}


static void MatrixPalleteCallBack(GLProgram* glProgram, Uniform* uniform,int paletteSize, const float* palette)

{

    glUniform4fv( uniform->location, (GLsizei)paletteSize, (constfloat*)palette );

}


void Effect3DOutline::draw(constMat4 &transform)

{

    //draw

    Color4F color(_sprite->getDisplayedColor());

    color.a = _sprite->getDisplayedOpacity() /255.0f;

    _glProgramState->setUniformVec4("u_color",Vec4(color.r, color.g, color.b, color.a));

    if(_sprite && _sprite->getMesh())

    {

        glEnable(GL_CULL_FACE);

        glCullFace(GL_FRONT);

        glEnable(GL_DEPTH_TEST);

        

        auto mesh = _sprite->getMesh();

        glBindBuffer(GL_ARRAY_BUFFER, mesh->getVertexBuffer());

        

        auto skin = _sprite->getMesh()->getSkin();

        if(_sprite && skin)

        {

            auto function = std::bind(MatrixPalleteCallBack,std::placeholders::_1,std::placeholders::_2,

                                      skin->getMatrixPaletteSize(), (float*)skin->getMatrixPalette());

            _glProgramState->setUniformCallback("u_matrixPalette", function);

        }

        

        if(_sprite)

            _glProgramState->apply(transform);

 

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->getIndexBuffer());

        glDrawElements(mesh->getPrimitiveType(), mesh->getIndexCount(), mesh->getIndexFormat(),0);

        CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1, mesh->getIndexCount());

        

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);

        glBindBuffer(GL_ARRAY_BUFFER,0);

        glDisable(GL_DEPTH_TEST);

        glCullFace(GL_BACK);

        glDisable(GL_CULL_FACE);

    }

}


void EffectSprite3D::draw(cocos2d::Renderer *renderer,const cocos2d::Mat4 &transform,uint32_t flags)

{

    for(auto &effect : _effects)

    {

        if(std::get<0>(effect) >=0)

            break;

        CustomCommand &cc = std::get<2>(effect);

        cc.func = CC_CALLBACK_0(Effect3D::draw,std::get<1>(effect),transform);

        renderer->addCommand(&cc);

        

    }

    

    if(!_defaultEffect)

    {

        Sprite3D::draw(renderer, transform, flags);

    }

    else

    {

        _command.init(_globalZOrder);

        _command.func =CC_CALLBACK_0(Effect3D::draw,_defaultEffect, transform);

        renderer->addCommand(&_command);

    }

    

    for(auto &effect : _effects)

    {

        if(std::get<0>(effect) <=0)

            continue;

        CustomCommand &cc = std::get<2>(effect);

        cc.func = CC_CALLBACK_0(Effect3D::draw,std::get<1>(effect),transform);

        renderer->addCommand(&cc);

        

    }

}

//Sprite3DEffectTest中实现了对Sprite3DEffect的加载

Sprite3DEffectTest::Sprite3DEffectTest()

{

    auto s =Director::getInstance()->getWinSize();

    addNewSpriteWithCoords(Vec2(s.width/2, s.height/2) );

    

    auto listener =EventListenerTouchAllAtOnce::create();

    listener->onTouchesEnded = CC_CALLBACK_2(Sprite3DEffectTest::onTouchesEnded,this);

    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener,this);

}


void Sprite3DEffectTest::addNewSpriteWithCoords(Vec2 p)

{

    //option 2: load obj and assign the texture

    auto sprite =EffectSprite3D::createFromObjFileAndTexture("Sprite3DTest/boss1.obj","Sprite3DTest/boss.png");

    Effect3DOutline* effect =Effect3DOutline::create();

    sprite->addEffect(effect, -1);

    effect->setOutlineColor(Vec3(1,0,0));

    effect->setOutlineWidth(0.01f);

    

    Effect3DOutline* effect2 =Effect3DOutline::create();

    sprite->addEffect(effect2, -2);

    effect2->setOutlineWidth(0.02f);

    effect2->setOutlineColor(Vec3(1,1,0));

    //sprite->setEffect3D(effect);

    sprite->setScale(6.f);

    

    //add to scene

    addChild( sprite );

    

    sprite->setPosition( Vec2( p.x, p.y) );

    

    ActionInterval* action;

    float random = CCRANDOM_0_1();

    

    if( random < 0.20 )

        action = ScaleBy::create(3,2);

    else if(random <0.40)

        action = RotateBy::create(3,360);

    else if( random <0.60)

        action = Blink::create(1,3);

    else if( random <0.8 )

        action = TintBy::create(2,0, -255, -255);

    else

        action = FadeOut::create(2);

    auto action_back = action->reverse();

    auto seq = Sequence::create( action, action_back,nullptr );

    

    sprite->runAction( RepeatForever::create(seq) );

}


void Sprite3DEffectTest::onTouchesEnded(conststd::vector<Touch*>& touches,Event* event)

{

    for (auto touch: touches)

    {

        auto location = touch->getLocation();

        

        addNewSpriteWithCoords( location );

    }

}

4.加载包含了3D纹理和动画的C3B文件,具体转化方法为在tools下使用fbx-conv.exe将3dmax导出的文件转化为c3b

用Sprite3D来加载c3b,用Animation3D,和Animate3D来创建动画

   auto animation = Animation3D::create(fileName);

    if (animation)

    {

        auto animate = Animate3D::create(animation);

       sprite->runAction(RepeatForever::create(animate));

 }


Cocos2d-x 3.3 的3D开发功能介绍_第4张图片

void Sprite3DWithSkinTest::addNewSpriteWithCoords(Vec2 p)

{

    std::string fileName ="Sprite3DTest/orc.c3b";

    auto sprite = EffectSprite3D::create(fileName);

    sprite->setScale(3);

    sprite->setRotation3D(Vec3(0,180,0));

    addChild(sprite);

    sprite->setPosition( Vec2( p.x, p.y) );

    auto animation = Animation3D::create(fileName);

    if (animation)

    {

        auto animate = Animate3D::create(animation);

        bool inverse = (std::rand() %3 == 0);


        int rand2 = std::rand();

        float speed = 1.0f;

        if(rand2 % 3 ==1)

        {

            speed = animate->getSpeed() + CCRANDOM_0_1();

        }

        else if(rand2 %3 == 2)

        {

            speed = animate->getSpeed() - 0.5 * CCRANDOM_0_1();

        }

        animate->setSpeed(inverse ? -speed : speed);


        sprite->runAction(RepeatForever::create(animate));

    }

}

5.在以上模型和动画中添加特效(方法和在静态模型上一样)

Cocos2d-x 3.3 的3D开发功能介绍_第5张图片


void Sprite3DWithSkinOutlineTest::addNewSpriteWithCoords(Vec2 p)

{

     std::string fileName ="Sprite3DTest/orc.c3b";

    auto sprite = EffectSprite3D::create(fileName);

    

    Effect3DOutline* effect =Effect3DOutline::create();

    effect->setOutlineColor(Vec3(1,0,0));

    effect->setOutlineWidth(0.01f);

    sprite->addEffect(effect, -1);


    

    Effect3DOutline* effect2 =Effect3DOutline::create();

    effect2->setOutlineWidth(0.02f);

    effect2->setOutlineColor(Vec3(1,1,0));

    sprite->addEffect(effect2, -2);


    

    sprite->setScale(3);

    sprite->setRotation3D(Vec3(0,180,0));

    addChild(sprite);

    sprite->setPosition( Vec2( p.x, p.y) );

    

    auto animation = Animation3D::create(fileName);

    if (animation)

    {

        auto animate = Animate3D::create(animation);

        bool inverse = (std::rand() %3 == 0);

        

        int rand2 = std::rand();

        float speed = 1.0f;

        if(rand2 % 3 ==1)

        {

            speed = animate->getSpeed() + CCRANDOM_0_1();

        }

        else if(rand2 %3 == 2)

        {

            speed = animate->getSpeed() - 0.5 * CCRANDOM_0_1();

        }

        animate->setSpeed(inverse ? -speed : speed);

        

        sprite->runAction(RepeatForever::create(animate));

    }

}

6.动画的切换

既然每个3D动画是Action就可以通过切换每个Sprite3D的Action来切换动画,下面是一个小乌龟的2个动画之间的切换

Cocos2d-x 3.3 的3D开发功能介绍_第6张图片

Animate3DTest::Animate3DTest()

: _hurt(nullptr)

, _swim(nullptr)

, _sprite(nullptr)

, _moveAction(nullptr)

, _elapseTransTime(0.f)

{ //添加小乌龟

    addSprite3D();

    

    auto listener =EventListenerTouchAllAtOnce::create();

    listener->onTouchesEnded = CC_CALLBACK_2(Animate3DTest::onTouchesEnded,this);

    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener,this);

    

    scheduleUpdate();

}


Animate3DTest::~Animate3DTest()

{

    CC_SAFE_RELEASE(_moveAction);

    CC_SAFE_RELEASE(_hurt);

    CC_SAFE_RELEASE(_swim);

}



void Animate3DTest::update(float dt)

{

    if (_state ==State::HURT_TO_SWIMMING)

    {

        _elapseTransTime += dt;

        

        if (_elapseTransTime >=Animate3D::getTransitionTime())

        {

            _sprite->stopAction(_hurt);

            _state = State::SWIMMING;

        }

    }

    elseif (_state ==State::SWIMMING_TO_HURT)

    {

        _elapseTransTime += dt;

        if (_elapseTransTime >=Animate3D::getTransitionTime())

        {

            _sprite->stopAction(_swim);

            _state = State::HURT;

        }

    }

}


void Animate3DTest::addSprite3D()

{

    std::string fileName ="Sprite3DTest/tortoise.c3b";

    auto sprite = Sprite3D::create(fileName);

    sprite->setScale(0.1f);

    auto s =Director::getInstance()->getWinSize();

    sprite->setPosition(Vec2(s.width *4.f / 5.f, s.height /2.f));

    addChild(sprite);

    _sprite = sprite;

    auto animation = Animation3D::create(fileName);

    if (animation)

    {   //2个动画的时间不同,这些在3Dmax中定义

        auto animate = Animate3D::create(animation, 0.f,1.933f);

        _swim = RepeatForever::create(animate);

        sprite->runAction(_swim);

        

        _swim->retain();

        _hurt = Animate3D::create(animation,1.933f, 2.8f);

        _hurt->retain();

        _state = State::SWIMMING;

    }

    

    _moveAction = MoveTo::create(4.f,Vec2(s.width / 5.f, s.height / 2.f));

    _moveAction->retain();

    auto seq = Sequence::create(_moveAction,CallFunc::create(CC_CALLBACK_0(Animate3DTest::reachEndCallBack,this)), nullptr);

    seq->setTag(100);

    sprite->runAction(seq);

}

//当触摸小乌龟则改变动画

void Animate3DTest::reachEndCallBack()

{

    _sprite->stopActionByTag(100);

    auto inverse = (MoveTo*)_moveAction->reverse();

    inverse->retain();

    _moveAction->release();

    _moveAction = inverse;

    auto rot = RotateBy::create(1.f,Vec3(0.f, 180.f,0.f));

    auto seq = Sequence::create(rot,_moveAction, CallFunc::create(CC_CALLBACK_0(Animate3DTest::reachEndCallBack,this)), nullptr);

    seq->setTag(100);

    _sprite->runAction(seq);

}


void Animate3DTest::renewCallBack()

{

    //rerun swim action

    _sprite->runAction(_swim);

    _state =State::HURT_TO_SWIMMING;

    _elapseTransTime = 0.0f;

}


void Animate3DTest::onTouchesEnded(conststd::vector<Touch*>& touches,Event* event)

{

    for (auto touch: touches)

    {

        auto location = touch->getLocation();

        

        if (_sprite)

        {

            float len = (_sprite->getPosition() - location).length();

            if (len < 40)

            {

                //hurt the tortoise 在游动状态改变为被抓住的动画

                if (_state ==State::SWIMMING)

                {

                    _elapseTransTime =0.0f;

                    _state = State::SWIMMING_TO_HURT;

                    _sprite->stopAction(_hurt);

                    _sprite->runAction(_hurt);

                    auto delay = DelayTime::create(_hurt->getDuration() -Animate3D::getTransitionTime());

                    auto seq = Sequence::create(delay, CallFunc::create(CC_CALLBACK_0(Animate3DTest::renewCallBack,this)), nullptr);

                    seq->setTag(101);

                    _sprite->runAction(seq);

                }

                return;

            }

        }

    }

}

7.动态添加武器

Cocos2d-x 3.3 的3D开发功能介绍_第7张图片

AttachmentTest::AttachmentTest()

: _hasWeapon(false)

, _sprite(nullptr)

{

    auto s =Director::getInstance()->getWinSize();

    addNewSpriteWithCoords(Vec2(s.width/2, s.height/2) );    

    auto listener =EventListenerTouchAllAtOnce::create();

    listener->onTouchesEnded = CC_CALLBACK_2(AttachmentTest::onTouchesEnded,this);

    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener,this);

}


void AttachmentTest::addNewSpriteWithCoords(Vec2 p)

{

    std::string fileName ="Sprite3DTest/orc.c3b";

    auto sprite = Sprite3D::create(fileName);

    sprite->setScale(5);

    sprite->setRotation3D(Vec3(0,180,0));

    addChild(sprite);

    sprite->setPosition( Vec2( p.x, p.y) );

    //test attach  亮点在这里,获取某个骨骼,Bip001 R Hand是在3Dmax定义的

    auto sp =Sprite3D::create("Sprite3DTest/axe.c3b");

    sprite->getAttachNode("Bip001 R Hand")->addChild(sp);

    

    auto animation = Animation3D::create(fileName);

    if (animation)

    {

        auto animate = Animate3D::create(animation);

        

        sprite->runAction(RepeatForever::create(animate));

    }

    _sprite = sprite;

    _hasWeapon = true;

}


void AttachmentTest::onTouchesEnded(conststd::vector<Touch*>& touches,Event* event)

{

    if (_hasWeapon)

    {

        _sprite->removeAllAttachNode();   //去掉新骨骼节点

    }

    else

    {

        auto sp = Sprite3D::create("Sprite3DTest/axe.c3b");

        _sprite->getAttachNode("Bip001 R Hand")->addChild(sp);

    }

    _hasWeapon = !_hasWeapon;

}

8.动态修改材质Mesh(这个demo好,美女的模型超赞)


Cocos2d-x 3.3 的3D开发功能介绍_第8张图片

Sprite3DReskinTest::Sprite3DReskinTest()

: _sprite(nullptr)

{

    auto s = Director::getInstance()->getWinSize();

    addNewSpriteWithCoords( Vec2(s.width/2, s.height/2) );

    

    auto listener = EventListenerTouchAllAtOnce::create();

    listener->onTouchesEnded = CC_CALLBACK_2(Sprite3DReskinTest::onTouchesEnded,this);

    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener,this);

    TTFConfig ttfConfig("fonts/arial.ttf",20);

    auto label1 = Label::createWithTTF(ttfConfig,"Hair");

    auto item1 = MenuItemLabel::create(label1,CC_CALLBACK_1(Sprite3DReskinTest::menuCallback_switchHair,this) );

    auto label2 = Label::createWithTTF(ttfConfig,"Glasses");

    auto item2 = MenuItemLabel::create(label2,CC_CALLBACK_1(Sprite3DReskinTest::menuCallback_switchGlasses,this) );

    auto label3 = Label::createWithTTF(ttfConfig,"Coat");

    auto item3 = MenuItemLabel::create(label3,CC_CALLBACK_1(Sprite3DReskinTest::menuCallback_switchCoat,this) );

    auto label4 = Label::createWithTTF(ttfConfig,"Pants");

    auto item4 = MenuItemLabel::create(label4,CC_CALLBACK_1(Sprite3DReskinTest::menuCallback_switchPants,this) );

    auto label5 = Label::createWithTTF(ttfConfig,"Shoes");

    auto item5 = MenuItemLabel::create(label5,CC_CALLBACK_1(Sprite3DReskinTest::menuCallback_switchShoes,this) );

    item1->setPosition( Vec2(VisibleRect::left().x+50, VisibleRect::bottom().y+item1->getContentSize().height*4 ) );

    item2->setPosition( Vec2(VisibleRect::left().x+50, VisibleRect::bottom().y+item1->getContentSize().height *5 ) );

    item3->setPosition( Vec2(VisibleRect::left().x+50, VisibleRect::bottom().y+item1->getContentSize().height*6 ) );

    item4->setPosition( Vec2(VisibleRect::left().x+50, VisibleRect::bottom().y+item1->getContentSize().height *7 ) );

    item5->setPosition( Vec2(VisibleRect::left().x+50, VisibleRect::bottom().y+item1->getContentSize().height *8 ) );

    auto pMenu1 = CCMenu::create(item1,item2,item3,item4,item5,NULL);

    pMenu1->setPosition(Vec2(0,0));

    this->addChild(pMenu1, 10);

    

}

void Sprite3DReskinTest::menuCallback_switchHair(Ref* sender)

{

    _useHairId++;

    if(_useHairId > 1 )

    {

        _useHairId = 0;

    }

    if(_useHairId >= 0  && _sprite)

    {

        for(int i =0; i < 2; i++ )

        {  //                           获取材质 可见3.3支持了多套材质

            auto subMesh = _sprite->getMeshByName(_girlHair[i]);

            if(subMesh)

            {

                if(i == _useHairId )

                {

                    subMesh->setVisible(true);

                }

                else

                {

                    subMesh->setVisible(false);

                }

            }

        }

    }

}

void Sprite3DReskinTest::menuCallback_switchGlasses(Ref* sender)

{

    auto subMesh = _sprite->getMeshByName("Girl_Glasses01");

    if(subMesh)

    {

        if(subMesh->isVisible())

        {

            subMesh->setVisible(false);

        }

        else

        {

            subMesh->setVisible(true);

        }

    }

}

void Sprite3DReskinTest::menuCallback_switchCoat(Ref* sender)

{

    _useUpBodyId++;

    if(_useUpBodyId > 1 )

    {

        _useUpBodyId = 0;

    }

    if(_useUpBodyId >= 0  && _sprite)

    {

        for(int i =0; i < 2; i++ )

        {

            auto subMesh = _sprite->getMeshByName(_girlUpperBody[i]);

            if(subMesh)

            {

                if(i == _useUpBodyId )

                {

                    subMesh->setVisible(true);

                }

                else

                {

                    subMesh->setVisible(false);

                }

            }

        }

    }

}

void Sprite3DReskinTest::menuCallback_switchPants(Ref* sender)

{

    _usePantsId++;

    if(_usePantsId > 1 )

    {

        _usePantsId = 0;

    }

    if(_usePantsId >= 0  && _sprite)

    {

        for(int i =0; i < 2; i++ )

        {

            auto subMesh = _sprite->getMeshByName(_girlPants[i]);

            if(subMesh)

            {

                if(i == _usePantsId )

                {

                    subMesh->setVisible(true);

                }

                else

                {

                    subMesh->setVisible(false);

                }

            }

        }

    }

}

void Sprite3DReskinTest::menuCallback_switchShoes(Ref* sender)

{

        _useShoesId++;

        if(_useShoesId > 1 )

        {

            _useShoesId = 0;

        }

        if(_useShoesId >= 0  && _sprite)

        {

            for(int i =0; i < 2; i++ )

            {

                auto subMesh = _sprite->getMeshByName(_girlShoes[i]);

                if(subMesh)

                {

                    if(i == _useShoesId )

                    {

                        subMesh->setVisible(true);

                    }

                    else

                    {

                        subMesh->setVisible(false);

                    }

                }

            }

        }

       

}

std::string Sprite3DReskinTest::title() const

{

    return"Testing Sprite3D Reskin";

}

std::string Sprite3DReskinTest::subtitle() const

{

    return "";

}


void Sprite3DReskinTest::addNewSpriteWithCoords(Vec2 p)

{

    _girlPants[0]= "Girl_LowerBody01";

    _girlPants[1]= "Girl_LowerBody02";

    _girlUpperBody[0] = "Girl_UpperBody01";

    _girlUpperBody[1] = "Girl_UpperBody02";

    _girlShoes[0]  = "Girl_Shoes01";

    _girlShoes[1]  = "Girl_Shoes02";

    _girlHair[0]= "Girl_Hair01";

    _girlHair[1]= "Girl_Hair02";

    _usePantsId = 0;

    _useUpBodyId = 0;

    _useShoesId   =0;

    _useHairId = 0;

    

    std::string fileName = "Sprite3DTest/ReskinGirl.c3b";

    auto sprite = Sprite3D::create(fileName);

    sprite->setScale(4);

    sprite->setRotation3D(Vec3(0,0,0));

    auto girlPants = sprite->getMeshByName(_girlPants[1]);

    if(girlPants)

    {

        girlPants->setVisible(false);

    }

    auto girlShoes = sprite->getMeshByName(_girlShoes[1]);

    if(girlShoes)

    {

        girlShoes->setVisible(false);

    }

    auto girlHair = sprite->getMeshByName(_girlHair[1]);

    if(girlHair)

    {

        girlHair->setVisible(false);

    }

    auto girlUpBody = sprite->getMeshByName( _girlUpperBody[1]);

    if(girlUpBody)

    {

        girlUpBody->setVisible(false);

    }

    addChild(sprite);

    sprite->setPosition( Vec2( p.x, p.y-60) );

    auto animation = Animation3D::create(fileName);

    if (animation)

    {

        auto animate = Animate3D::create(animation);

        

        sprite->runAction(RepeatForever::create(animate));

    }

    _sprite = sprite;

}


9.包围盒与3D模型碰撞的实现

AABB碰撞原理参考以下网址

http://cn.cocos2d-x.org/tutorial/show?id=1572

Cocos2d-x 3.3 的3D开发功能介绍_第9张图片

Sprite3DWithOBBPerfromanceTest::Sprite3DWithOBBPerfromanceTest()

{

    auto listener = EventListenerTouchAllAtOnce::create();

    listener->onTouchesBegan = CC_CALLBACK_2(Sprite3DWithOBBPerfromanceTest::onTouchesBegan,this);

    listener->onTouchesEnded = CC_CALLBACK_2(Sprite3DWithOBBPerfromanceTest::onTouchesEnded,this);

    listener->onTouchesMoved = CC_CALLBACK_2(Sprite3DWithOBBPerfromanceTest::onTouchesMoved,this);

    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener,this);

    auto s = Director::getInstance()->getWinSize();

    initDrawBox(); 

    addNewSpriteWithCoords(Vec2(s.width/2, s.height/2));

    MenuItemFont::setFontName("fonts/arial.ttf");

    MenuItemFont::setFontSize(65);

    auto decrease = MenuItemFont::create(" - ", CC_CALLBACK_1(Sprite3DWithOBBPerfromanceTest::delOBBCallback,this));

    decrease->setColor(Color3B(0,200,20));

    auto increase = MenuItemFont::create(" + ", CC_CALLBACK_1(Sprite3DWithOBBPerfromanceTest::addOBBCallback,this));

    increase->setColor(Color3B(0,200,20));

    

    auto menu = Menu::create(decrease, increase,nullptr);

    menu->alignItemsHorizontally();

    menu->setPosition(Vec2(s.width/2, s.height-65));

    addChild(menu, 1);

    

    TTFConfig ttfCount("fonts/Marker Felt.ttf",30);

    _labelCubeCount = Label::createWithTTF(ttfCount,"0 cubes");

    _labelCubeCount->setColor(Color3B(0,200,20));

    _labelCubeCount->setPosition(Vec2(s.width/2, s.height-90));

    addChild(_labelCubeCount);

    _hasCollider = false;

    addOBBCallback(nullptr);

    scheduleUpdate();

}

std::string Sprite3DWithOBBPerfromanceTest::title() const

{

    return"OBB Collison Perfromance Test";

}

std::string Sprite3DWithOBBPerfromanceTest::subtitle() const

{

    return "";

}

void Sprite3DWithOBBPerfromanceTest::addNewOBBWithCoords(Vec2 p)

{

    Vec3 extents = Vec3(10, 10, 10);

    AABB aabb(-extents, extents);

    auto obb = OBB(aabb);

    obb._center = Vec3(p.x,p.y,0);

    _obb.push_back(obb);

}


void Sprite3DWithOBBPerfromanceTest::onTouchesBegan(const std::vector& touches, Event* event)

{

    for (auto touch: touches)

    {

        auto location = touch->getLocationInView();

        

        if(_obb.size() > 0)

        {

            _intersetList.clear();

            Ray ray;

            calculateRayByLocationInView(&ray,location);

            for(int i =0; i < _obb.size(); i++)

            {

                if(ray.intersects(_obb[i]))

                {

                    _intersetList.insert(i);

                    return;

                }

            }

        }

    }

}


void Sprite3DWithOBBPerfromanceTest::onTouchesEnded(const std::vector& touches, Event* event)

{

    

}


void Sprite3DWithOBBPerfromanceTest::onTouchesMoved(const std::vector& touches, Event* event)

{

    for (auto touch: touches)

    {

        auto location = touch->getLocation();

        

        for(int i =0; i < _obb.size(); i++)

        {

            if(_intersetList.find(i) != _intersetList.end())

                _obb[i]._center = Vec3(location.x,location.y,0);

        }

    }

}


void Sprite3DWithOBBPerfromanceTest::update(float dt)

{

    char szText[16];

    sprintf(szText,"%lu cubes",_obb.size());

    _labelCubeCount->setString(szText);

    

    if (_drawDebug)

    {

        _drawDebug->clear();

        

        Mat4 mat = _sprite->getNodeToWorldTransform();

        mat.getRightVector(&_obbt._xAxis);

        _obbt._xAxis.normalize();

        

        mat.getUpVector(&_obbt._yAxis);

        _obbt._yAxis.normalize();

        

        mat.getForwardVector(&_obbt._zAxis);

        _obbt._zAxis.normalize();

        

        _obbt._center = _sprite->getPosition3D();

        

        Vec3 corners[8] = {};

        _obbt.getCorners(corners);

        _drawDebug->drawCube(corners, Color4F(0,0,1,1));

    }

    if(_obb.size() > 0)

    {

        _drawOBB->clear();

        for(int i =0; i < _obb.size(); i++)

        {

            Vec3 corners[8] = {};

            _obb[i].getCorners(corners);

            _drawOBB->drawCube(corners, _obbt.intersects(_obb[i])?Color4F(1,0,0,1):Color4F(0,1,0,1));

        }

    }

}


void Sprite3DWithOBBPerfromanceTest::initDrawBox()

{

    _drawOBB = DrawNode3D::create();

    addChild(_drawOBB);

}


void Sprite3DWithOBBPerfromanceTest::addNewSpriteWithCoords(Vec2 p)

{

    std::string fileName = "Sprite3DTest/tortoise.c3b";

    auto sprite = Sprite3D::create(fileName);

    sprite->setScale(0.1f);

    auto s = Director::getInstance()->getWinSize();

    sprite->setPosition(Vec2(s.width * 4.f /5.f, s.height / 2.f));

    addChild(sprite);

    _sprite = sprite;

    auto animation = Animation3D::create(fileName);

    if (animation)

    {

        auto animate = Animate3D::create(animation,0.f, 1.933f);

        sprite->runAction(RepeatForever::create(animate));

    }

    

    _moveAction = MoveTo::create(4.f, Vec2(s.width /5.f, s.height / 2.f));

    _moveAction->retain();

    auto seq = Sequence::create(_moveAction, CallFunc::create(CC_CALLBACK_0(Sprite3DWithOBBPerfromanceTest::reachEndCallBack,this)), nullptr);

    seq->setTag(100);

    sprite->runAction(seq);

    

    AABB aabb = _sprite->getAABB();

    _obbt = OBB(aabb);

    

    _drawDebug = DrawNode3D::create();

    addChild(_drawDebug);

}


void Sprite3DWithOBBPerfromanceTest::reachEndCallBack()

{

    _sprite->stopActionByTag(100);

    auto inverse = (MoveTo*)_moveAction->reverse();

    inverse->retain();

    _moveAction->release();

    _moveAction = inverse;

    auto rot = RotateBy::create(1.0f, Vec3(0.f,180.f, 0.f));

    auto seq = Sequence::create(rot, _moveAction, CallFunc::create(CC_CALLBACK_0(Sprite3DWithOBBPerfromanceTest::reachEndCallBack,this)), nullptr);

    seq->setTag(100);

    _sprite->runAction(seq);

}


void Sprite3DWithOBBPerfromanceTest::addOBBCallback(Ref* sender)

{

    addOBBWithCount(10);

}


void Sprite3DWithOBBPerfromanceTest::addOBBWithCount(float value)

{

    for(int i = 0; i < value; i++)

    {

        Vec2 randompos = Vec2(CCRANDOM_0_1() * Director::getInstance()->getWinSize().width,CCRANDOM_0_1() * Director::getInstance()->getWinSize().height);

        Vec3 extents = Vec3(10, 10, 10);

        AABB aabb(-extents, extents);

        auto obb = OBB(aabb);

        obb._center = Vec3(randompos.x,randompos.y,0);

        _obb.push_back(obb);

    }

}


void Sprite3DWithOBBPerfromanceTest::delOBBCallback(Ref* sender)

{

    delOBBWithCount(10);

}


void Sprite3DWithOBBPerfromanceTest::delOBBWithCount(float value)

{

    if(_obb.size() >= 10)

    {

        _obb.erase(_obb.begin(),_obb.begin() + value);

        _drawOBB->clear();

    }

    else

        return;

}

void Sprite3DWithOBBPerfromanceTest::unproject(const Mat4& viewProjection,const Size* viewport, Vec3* src, Vec3* dst)

{

    assert(dst);

    

    assert(viewport->width != 0.0f && viewport->height !=0.0f);

    Vec4 screen(src->x / viewport->width, ((viewport->height - src->y)) / viewport->height, src->z,1.0f);

    

    screen.x = screen.x * 2.0f - 1.0f;

    screen.y = screen.y * 2.0f - 1.0f;

    screen.z = screen.z * 2.0f - 1.0f;

    

    viewProjection.getInversed().transformVector(screen, &screen);

    

    if (screen.w != 0.0f)

    {

        screen.x /= screen.w;

        screen.y /= screen.w;

        screen.z /= screen.w;

    }

    

    dst->set(screen.x, screen.y, screen.z);

}


void Sprite3DWithOBBPerfromanceTest::calculateRayByLocationInView(Ray* ray,const Vec2& location)

{

    auto dir = Director::getInstance();

    auto view = dir->getWinSize();

    Mat4 mat = dir->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

    mat = dir->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);

    

    Vec3 src = Vec3(location.x, location.y, -1);

    Vec3 nearPoint;

    unproject(mat, &view, &src, &nearPoint);

    

    src = Vec3(location.x, location.y, 1);

    Vec3 farPoint;

    unproject(mat, &view, &src, &farPoint);

    

    Vec3 direction;

    Vec3::subtract(farPoint, nearPoint, &direction);

    direction.normalize();

    

    ray->_origin = nearPoint;

    ray->_direction = direction;

}

10.3D模型的镜像

Cocos2d-x 3.3 的3D开发功能介绍_第10张图片

Sprite3DMirrorTest::Sprite3DMirrorTest()

: _sprite(nullptr)

, _mirrorSprite(nullptr)

{

    auto s = Director::getInstance()->getWinSize();

    addNewSpriteWithCoords( Vec2(s.width/2, s.height/2) );

}

std::string Sprite3DMirrorTest::title() const

{

    return"Sprite3D Mirror Test";

}

std::string Sprite3DMirrorTest::subtitle() const

{

    return "";

}


void Sprite3DMirrorTest::addNewSpriteWithCoords(Vec2 p)

{

    std::string fileName = "Sprite3DTest/orc.c3b";

    auto sprite = Sprite3D::create(fileName);

    sprite->setScale(5);

    sprite->setRotation3D(Vec3(0,180,0));

    addChild(sprite);

    sprite->setPosition( Vec2( p.x - 80, p.y) );

    

    //test attach

    auto sp = Sprite3D::create("Sprite3DTest/axe.c3b");

    sprite->getAttachNode("Bip001 R Hand")->addChild(sp);

    

    auto animation = Animation3D::create(fileName);

    if (animation)

    {

        auto animate = Animate3D::create(animation);

        

        sprite->runAction(RepeatForever::create(animate));

    }

    _sprite = sprite;

    _hasWeapon = true;

    

    //create mirror Sprite3D 镜像 

    sprite = Sprite3D::create(fileName);

    sprite->setScale(5);

    sprite->setScaleX(-5);

    sprite->setCullFace(GL_FRONT);

    sprite->setRotation3D(Vec3(0,180,0));

    addChild(sprite);

    sprite->setPosition( Vec2( p.x + 80, p.y) );

    

    //test attach

    sp = Sprite3D::create("Sprite3DTest/axe.c3b");

    sprite->getAttachNode("Bip001 R Hand")->addChild(sp);

    

    animation = Animation3D::create(fileName);

    if (animation)

    {

        auto animate = Animate3D::create(animation);

        

        sprite->runAction(RepeatForever::create(animate));

    }

    _mirrorSprite = sprite;

}


你可能感兴趣的:(cocos2d-x,Cocos2d-x,3.0final,自学教程)