Historique des modifications - Message

Message #8260

Sujet: Texture sur un ISceneNode personnalisé


TypeDateAuteurContenu
Création du message07-06-2010 16:44:31ordiman85
Bonjour,

Je débute sur irrlicht.

J'ai dérivé un ISceneNode pour créer un objet personnalisé (TubeSceneNode) et j'aimerais le texturer. J'ai essayé de modifier "m_material" sans succès.
J'aimerais de votre aide svp pour m'expliquer comment je pourrais faire.

Mon programme affiche un tube formé à partir de quelques points, le chemin du tube est calculé par l'interpolation de Hermite. Vous pouvez vous déplacer avec la caméra FPS standard.

Voici le code complet :

Code c++ :


#include <irrlicht.h>
#include <cmath>

using namespace irr;

/**
 * Stocke une liste de points et calcule un chemin fluide par interpolation de Hermite
 */
class Path3D
{
public:
    /*
        Hermite interpolation
        Tension: 1 is high, 0 normal, -1 is low
        Bias: 0 is even,
            positive is towards first segment,
            negative towards the other
        Source:
            http://local.wasp.uwa.edu.au/~pbourke/miscellaneous/interpolation/
    */
    Path3D(double tension = 0, double bias = 0) :
        m_tension(tension), m_bias(bias)
    {
    }

    void push(const core::vector3df& v)
    {
        m_array.push_back(v);
    }

    int size() const
    {
        return m_array.size();
    }

    /**
     * mu : Partie entière = n° intervalle, partie décimale = pourcentage parcouru dans l'intervalle
     */
    core::vector3df getPoint(double mu) const
    {
        core::vector3df v1, v2, v3, v4;
        int index;
        double mu_interval, intpart;

        mu_interval = modf(mu, &intpart);
        index = (int)intpart;

        return getPoint(index, mu_interval);
    }

    /**
     * index : n° intervalle
     * mu : pourcentage parcouru dans l'intervalle
     */
    core::vector3df getPoint(int index, double mu) const
    {
        core::vector3df v1, v2, v3, v4;

        v1 = getVectorById(index - 1);
        v2 = getVectorById(index);
        v3 = getVectorById(index + 1);
        v4 = getVectorById(index + 2);

        return HermiteInterpolateVector(v1, v2, v3, v4, mu);
    }

private:
    core::vector3df getVectorById(int id) const
    {
        if (m_array.empty())
            return core::vector3df(0, 0, 0);
        else
            return m_array[id % m_array.size()];
    }

    core::vector3df HermiteInterpolateVector(
            const core::vector3df& v1,
            const core::vector3df& v2,
            const core::vector3df& v3,
            const core::vector3df& v4,
            double mu) const
    {
        core::vector3df w;
        w.X = HermiteInterpolate(v1.X, v2.X, v3.X, v4.X, mu);
        w.Y = HermiteInterpolate(v1.Y, v2.Y, v3.Y, v4.Y, mu);
        w.Z = HermiteInterpolate(v1.Z, v2.Z, v3.Z, v4.Z, mu);
        return w;
    }

    double HermiteInterpolate(double y0, double y1, double y2, double y3, double mu) const
    {
        double m0, m1, mu2, mu3;
        double a0, a1, a2, a3;

        mu2 = mu * mu;
        mu3 = mu2 * mu;
        m0  = (y1 - y0)*(1 + m_bias)*(1 - m_tension)/2;
        m0 += (y2 - y1)*(1 - m_bias)*(1 - m_tension)/2;
        m1  = (y2 - y1)*(1 + m_bias)*(1 - m_tension)/2;
        m1 += (y3 - y2)*(1 - m_bias)*(1 - m_tension)/2;
        a0 =  2*mu3 - 3*mu2 + 1;
        a1 =    mu3 - 2*mu2 + mu;
        a2 =    mu3 -   mu2;
        a3 = -2*mu3 + 3*mu2;

        return (a0*y1 + a1*m0 + a2*m1 + a3*y2);
    }

private:
    double m_tension;
    double m_bias;
    core::array<core::vector3df> m_array;
};


/**
 * Fabrique un tube à partir d'une liste de points (Path3D) en les reliant avec des virages arrondis
 * path : L'objet Path3D contenant la liste des points
 * radius : Le rayon du tube
 * pathDivide : Le nombre de sections entre 2 points (minimum: 1, un grand nombre augmente le détail)
 * ringDivide : Le nombre de côtés d'une section du tube (minimum: 8, un grand nombre augmente le détail)
 */
class TubeSceneNode : public scene::ISceneNode
{
public:
    TubeSceneNode(scene::ISceneNode *parent, scene::ISceneManager *mgr, s32 id, const Path3D& path, int radius, int pathDivide = 16, int ringDivide = 16)
        : scene::ISceneNode(parent, mgr, id)
    {
        core::vector3df u, v, A, B, dir, pt;

        m_material.Wireframe = false;
        m_material.Lighting = false;
        m_material.BackfaceCulling = false;

        // Calcule le nombre de cercles composant le tube
        m_ringDivide = ringDivide;
        m_verticesCount = path.size() * pathDivide * m_ringDivide;
        m_vertices = new video::S3DVertex[m_verticesCount];

        for (int i = 0; i < path.size(); i++)
        {
            for (int j = 0; j < pathDivide; j++)
            {
                // Calcule le vecteur directeur entre 2 points distincts
                A = path.getPoint(f64(i) + ( f64(j) / f64(pathDivide) ));
                B = path.getPoint(f64(i) + ( f64(j + 1) / f64(pathDivide) ));
                dir = (B - A).normalize();

                // Construit une nouvelle base (dir, u, v)
                u = (core::vector3df(dir.Y, -dir.X, 0)).normalize();
                v = (dir.crossProduct(u)).normalize();

                int indexRef = i * pathDivide * m_ringDivide + j * m_ringDivide;

                for (int k = 0; k < m_ringDivide; k++)
                {
                    // Définit les points d'un cercle entourant le vecteur directeur, qui fera partie du tube matériel
                    double teta = k * 2.0 * M_PI / m_ringDivide;
                    pt = A + ( radius * cos(teta) ) * u + ( radius * sin(teta) ) * v;
                    m_vertices[indexRef + k] = video::S3DVertex(pt, core::vector3df(0, 0, 0), video::SColor(128, 128, 255, 255), core::vector2d< f32 >(0, 0));
                }
            }
        }
    }

    virtual void OnRegisterSceneNode()
    {
        if (IsVisible)
            SceneManager->registerNodeForRendering(this);
        ISceneNode::OnRegisterSceneNode();
    }

    virtual void render()
    {
        u16 indices[6*m_verticesCount];
        video::IVideoDriver* driver;

        // Relie la moitié des triangles
        for (int i = 0; i < m_verticesCount; i++)
        {
            indices[3*i] = i % m_verticesCount;
            indices[3*i + 1] = ( i + 1 ) % m_verticesCount;
            indices[3*i + 2] = ( i + m_ringDivide ) % m_verticesCount;
        }

        // Relie l'autre moitié
        int j = m_verticesCount * 3;
        for (int i = 0; i < m_verticesCount; i++)
        {
            indices[j + 3*i] = ( i + 1 ) % m_verticesCount;
            indices[j + 3*i + 1] = ( i + m_ringDivide ) % m_verticesCount;
            indices[j + 3*i + 2] = ( i + m_ringDivide + 1 ) % m_verticesCount;
        }

        driver = SceneManager->getVideoDriver();
        driver->setMaterial(m_material);
        driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
        driver->drawIndexedTriangleList(m_vertices, m_verticesCount, &indices[0], m_verticesCount*2);
    }

    virtual const core::aabbox3d<f32>& getBoundingBox() const
    {
        return m_box;
    }

    virtual u32 getMaterialCount() const
    {
        return 1;
    }

    virtual video::SMaterial& getMaterial(u32 i)
    {
        return m_material;
    }

private:
    core::aabbox3d<irr::f32>    m_box;
    video::SMaterial            m_material;
    video::S3DVertex            *m_vertices;
    int                         m_verticesCount;
    int                         m_pathDivide;
    int                         m_ringDivide;
};


/**
 * Dessine seulement des traits des traits qui suivent le tunnel (pour débug)
 */
void drawTubeLines(video::IVideoDriver *driver, const Path3D &path, int precision)
{
    core::vector3df w1, w2;
    core::vector3df u, v, w;

    for (int i = 0; i < path.size(); i++)
    {
        for (int j = 0; j <= precision - 1; j++)
        {
            w1 = path.getPoint(i, f64(j) / f64(precision));
            w2 = path.getPoint(i, f64(j + 1) / f64(precision));
            driver->draw3DLine(w1, w2, video::SColor(0, 255*( f64(j) / f64(precision) ), 255*( f64(j) / f64(precision) ), 0 ));

            // Nouvelle base
            u = (w2 - w1).normalize();
            v = (core::vector3df(u.Y, -u.X, 0)).normalize();
            w = (u.crossProduct(v)).normalize();

            // Repère local (u, v, w)
            driver->draw3DLine(w1, w1 + 40 * u, video::SColor(0, 128, 0, 0 ));
            driver->draw3DLine(w1, w1 + 40 * v, video::SColor(0, 0, 128, 0 ));
            driver->draw3DLine(w1, w1 + 40 * w, video::SColor(0, 0, 0, 128 ));

            // Dessine un cercle
            int radius = 80;
            int circleDetail = 16;
            for (int k = 0; k < circleDetail; k++)
            {
                double teta = k * 2 * M_PI / circleDetail;
                double teta2 = (k + 1) * 2 * M_PI / circleDetail;

                core::vector3df u1, u2;
                u1 = w1 + radius * cos(teta) * v + radius * sin(teta) * w;
                u2 = w1 + radius * cos(teta2) * v + radius * sin(teta2) * w;
                driver->draw3DLine(u1, u2, video::SColor(0, 0, 128, 128 ));
            }
        }
    }
}


int main(void)
{
    IrrlichtDevice *device;
    video::IVideoDriver *driver;
    scene::ISceneManager *sceneManager;
    gui::IGUIEnvironment *gui;
    scene::ICameraSceneNode *camera;
    gui::IGUIStaticText *coordText;
    Path3D path;

    device = createDevice(video::EDT_OPENGL, core::dimension2d<u32>(800, 600), 32, false, false, false, 0);

    driver          = device->getVideoDriver();
    sceneManager    = device->getSceneManager();
    gui             = device->getGUIEnvironment();
    camera          = sceneManager->addCameraSceneNodeFPS();

    // Pour afficher les coordonnées
    coordText = gui->addStaticText(L"Coordonnees", core::rect<s32>(10, 10, 100, 45), true, true, 0, -1, true);

    // Le chemin suivi par le tube
    path.push(core::vector3df(80, 80, 80));
    path.push(core::vector3df(500, 200, 200));
    path.push(core::vector3df(500, 1000, 0));
    path.push(core::vector3df(200, 1000, 600));

    // Création du tube
    TubeSceneNode *tube = new TubeSceneNode(sceneManager->getRootSceneNode(), sceneManager, 666, path, 80);
    tube->drop();

    // Pour affichage des coordonnées
    core::vector3df posCam;
    wchar_t buffer[128];

    // Pour le dessin des lignes servant pour le débug
    video::SMaterial material;
    material.Lighting = false;

    while (device->run())
    {
        driver->beginScene(true, true, video::SColor(255, 255, 255, 255));

        sceneManager->drawAll();
        gui->drawAll();

        driver->setMaterial(material);

        // Dessin d'un repère orthonormé (x, y, z)
        driver->draw3DLine(core::vector3df(0, 0, 0), core::vector3df(100, 0, 0), video::SColor(0, 255, 0, 0));
        driver->draw3DLine(core::vector3df(0, 0, 0), core::vector3df(0, 100, 0), video::SColor(0, 0, 255, 0));
        driver->draw3DLine(core::vector3df(0, 0, 0), core::vector3df(0, 0, 100), video::SColor(0, 0, 0, 255));

        // Débug (commenter pour ne pas afficher les lignes de débug)
        drawTubeLines(driver, path, 16);

        driver->endScene();

        // Actualise les coordonnées et les affiche
        posCam = camera->getPosition();
        swprintf(buffer, 128, L"X : %f\
Y : %f\
Z : %f", posCam.X, posCam.Y, posCam.Z);
        coordText->setText(buffer);
    }

    device->drop();
    return 0;
}


Merci.

Cordialement, Charly

Retour

OptionsLiens officielsCaractéristiquesStatistiquesCommunauté
Préférences cookies
Corrections
irrlicht
irrklang
irredit
irrxml
Propulsé par Django
xhtml 1.0
css 2.1
884 membres
1441 sujets
11339 messages
Dernier membre inscrit: Saidov17
98 invités en ligne
membre en ligne: -
RSS Feed