Ejemplo n.º 1
0
/** RubberBand constructor. It creates a simple quad and attaches it to the
 *  root(!) of the graph. It's easier this way to get the right coordinates
 *  than attaching it to the plunger or kart, and trying to find the other
 *  coordinate.
 *  \param plunger Pointer to the plunger (non const, since the rubber band
 *                 can trigger an explosion)
 *  \param kart    Reference to the kart.
 */
RubberBand::RubberBand(Plunger *plunger, AbstractKart *kart)
          : m_plunger(plunger), m_owner(kart)
{
    const video::SColor color(77, 179, 0, 0);
    video::SMaterial m;
    m.AmbientColor    = color;
    m.DiffuseColor    = color;
    m.EmissiveColor   = color;
    m.BackfaceCulling = false;
    m_mesh           = irr_driver->createQuadMesh(&m, /*create_one_quad*/ true);
    m_buffer         = m_mesh->getMeshBuffer(0);
    m_attached_state = RB_TO_PLUNGER;
    assert(m_buffer->getVertexType()==video::EVT_STANDARD);

    // Set the vertex colors properly, as the new pipeline doesn't use the old light values
    u32 i;
    scene::IMeshBuffer * const mb = m_mesh->getMeshBuffer(0);
    video::S3DVertex * const verts = (video::S3DVertex *) mb->getVertices();
    const u32 max = mb->getVertexCount();
    for (i = 0; i < max; i++)
    {
        verts[i].Color = color;
    }

    // Color
    mb->getMaterial().setTexture(0, getUnicolorTexture(video::SColor(255, 255, 255, 255)));
    // Gloss
    mb->getMaterial().setTexture(1, getUnicolorTexture(video::SColor(0, 0, 0, 0)));
    // Colorization mask
    mb->getMaterial().setTexture(7, getUnicolorTexture(video::SColor(0, 0, 0, 0)));
    updatePosition();
    m_node = irr_driver->addMesh(m_mesh, "rubberband");
    irr_driver->applyObjectPassShader(m_node);
    if (STKMeshSceneNode *stkm = dynamic_cast<STKMeshSceneNode *>(m_node))
        stkm->setReloadEachFrame(true);
#ifdef DEBUG
    std::string debug_name = m_owner->getIdent()+" (rubber-band)";
    m_node->setName(debug_name.c_str());
#endif

}   // RubberBand
Ejemplo n.º 2
0
static void
SetTexture(GLMesh &mesh, unsigned i, bool isSrgb)
{
    if (!mesh.textures[i])
        mesh.textures[i] = getUnicolorTexture(video::SColor(255, 255, 255, 255));
    compressTexture(mesh.textures[i], isSrgb);
    if (UserConfigParams::m_azdo)
    {
        if (!mesh.TextureHandles[i])
            mesh.TextureHandles[i] = glGetTextureSamplerHandleARB(getTextureGLuint(mesh.textures[i]), MeshShader::ObjectPass1Shader::getInstance()->SamplersId[0]);
        if (!glIsTextureHandleResidentARB(mesh.TextureHandles[i]))
            glMakeTextureHandleResidentARB(mesh.TextureHandles[i]);
    }
}
Ejemplo n.º 3
0
// ----------------------------------------------------------------------------
void CheckLine::changeDebugColor(bool is_active)
{
    assert(m_debug_node);

    scene::IMesh *mesh         = m_debug_node->getMesh();
    scene::IMeshBuffer *buffer = mesh->getMeshBuffer(0);
    irr::video::S3DVertex* vertices
                               = (video::S3DVertex*)buffer->getVertices();
    video::SColor color = is_active ? video::SColor(192, 255, 0, 0)
                                    : video::SColor(192, 128, 128, 128);
    for(unsigned int i=0; i<4; i++)
    {
        vertices[i].Color = color;
    }
    buffer->getMaterial().setTexture(0, getUnicolorTexture(color));

}   // changeDebugColor
Ejemplo n.º 4
0
/** Constructor for a checkline.
 *  \param node XML node containing the parameters for this checkline.
 *  \param index Index of this check structure in the check manager.
 */
CheckLine::CheckLine(const XMLNode &node,  unsigned int index)
         : CheckStructure(node, index)
{
    // Note that when this is called the karts have not been allocated
    // in world, so we can't call world->getNumKarts()
    m_previous_sign.resize(race_manager->getNumberOfKarts());
    std::string p1_string("p1");
    std::string p2_string("p2");

    // In case of a cannon in a reverse track, we have to use the target line
    // as check line
    if(getType()==CT_CANNON && race_manager->getReverseTrack())
    {
        p1_string = "target-p1";
        p2_string = "target-p2";
    }
    core::vector2df p1, p2;
    if(node.get(p1_string, &p1)   &&
        node.get(p2_string, &p2)  &&
        node.get("min-height", &m_min_height))
    {
        m_left_point  = Vec3(p1.X, m_min_height, p1.Y);
        m_right_point = Vec3(p2.X, m_min_height, p2.Y);
    }
    else
    {
        node.get(p1_string, &m_left_point);
        p1 = core::vector2df(m_left_point.getX(), m_left_point.getZ());
        node.get(p2_string, &m_right_point);
        p2 = core::vector2df(m_right_point.getX(), m_right_point.getZ());
        m_min_height = std::min(m_left_point.getY(), m_right_point.getY());
    }
    m_line.setLine(p1, p2);
    if(UserConfigParams::m_check_debug)
    {
        video::SMaterial material;
        material.setFlag(video::EMF_BACK_FACE_CULLING, false);
        material.setFlag(video::EMF_LIGHTING, false);
        material.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
        scene::IMesh *mesh = irr_driver->createQuadMesh(&material,
                                                        /*create mesh*/true);
        scene::IMeshBuffer *buffer = mesh->getMeshBuffer(0);

        assert(buffer->getVertexType()==video::EVT_STANDARD);
        irr::video::S3DVertex* vertices
            = (video::S3DVertex*)buffer->getVertices();
        vertices[0].Pos = core::vector3df(p1.X,
                                          m_min_height-m_under_min_height,
                                          p1.Y);
        vertices[1].Pos = core::vector3df(p2.X,
                                          m_min_height-m_under_min_height,
                                          p2.Y);
        vertices[2].Pos = core::vector3df(p2.X,
                                          m_min_height+m_over_min_height,
                                          p2.Y);
        vertices[3].Pos = core::vector3df(p1.X,
                                          m_min_height+m_over_min_height,
                                          p1.Y);
        for(unsigned int i=0; i<4; i++)
        {
            vertices[i].Color = m_active_at_reset
                              ? video::SColor(128, 255, 0, 0)
                              : video::SColor(128, 128, 128, 128);
        }
        buffer->recalculateBoundingBox();
        buffer->getMaterial().setTexture(0, getUnicolorTexture(video::SColor(128, 255, 105, 180)));
        buffer->getMaterial().setTexture(1, getUnicolorTexture(video::SColor(0, 0, 0, 0)));
        buffer->getMaterial().setTexture(7, getUnicolorTexture(video::SColor(0, 0, 0, 0)));
        buffer->getMaterial().BackfaceCulling = false;
        //mesh->setBoundingBox(buffer->getBoundingBox());
        m_debug_node = irr_driver->addMesh(mesh, "checkdebug");
        mesh->drop();
    }
    else
    {
        m_debug_node = NULL;
    }
}   // CheckLine
Ejemplo n.º 5
0
/** Creates a mesh for this graph. The mesh is not added to a scene node and
 *  is stored in m_mesh.
 *  \param show_invisble  If true, also create a mesh for parts of the
 *         driveline that are invisible.
 *  \param enable_transparency Enable alpha blending to make the mesh
 *         semi transparent.
 *  \param track_color Colour of the actual quads.
 *  \param lap_color If defined, show the lap counting line in that colour.
 */
void QuadGraph::createMesh(bool show_invisible,
                           bool enable_transparency,
                           const video::SColor *track_color,
                           const video::SColor *lap_color)
{
    // The debug track will not be lighted or culled.
    video::SMaterial m;
    m.BackfaceCulling  = false;
    m.Lighting         = false;
    if(enable_transparency)
        m.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
    m.setTexture(0, getUnicolorTexture(SColor(255, 255, 255, 255)));
    m.setTexture(1, getUnicolorTexture(SColor(0, 0, 0, 0)));
    m_mesh             = irr_driver->createQuadMesh(&m);
    m_mesh_buffer      = m_mesh->getMeshBuffer(0);
    assert(m_mesh_buffer->getVertexType()==video::EVT_STANDARD);

    // Count the number of quads to display (some quads might be invisible
    unsigned int  n = 0;
    for(unsigned int i=0; i<m_all_nodes.size(); i++)
    {
        if(show_invisible || !m_all_nodes[i]->getQuad().isInvisible())
            n++;
    }

    // Four vertices for each of the n-1 remaining quads
    video::S3DVertex *new_v = new video::S3DVertex[4*n];
    // Each quad consists of 2 triangles with 3 elements, so
    // we need 2*3 indices for each quad.
    irr::u16         *ind   = new irr::u16[6*n];
    video::SColor     c(255, 255, 0, 0);

    if(track_color)
        c = *track_color;

    // Now add all quads
    int i=0;
    for(unsigned int count=0; count<m_all_nodes.size(); count++)
    {
        // Ignore invisible quads
        if(!show_invisible && m_all_nodes[count]->getQuad().isInvisible())
            continue;
        // Swap the colours from red to blue and back
        if(!track_color)
        {
            c.setRed ((i%2) ? 255 : 0);
            c.setBlue((i%2) ? 0 : 255);
        }
        // Transfer the 4 points of the current quad to the list of vertices
        m_all_nodes[count]->getQuad().getVertices(new_v+4*i, c);

        // Set up the indices for the triangles
        // (note, afaik with opengl we could use quads directly, but the code
        // would not be portable to directx anymore).
        ind[6*i  ] = 4*i+2;  // First triangle: vertex 0, 1, 2
        ind[6*i+1] = 4*i+1;
        ind[6*i+2] = 4*i;
        ind[6*i+3] = 4*i+3;  // second triangle: vertex 0, 1, 3
        ind[6*i+4] = 4*i+2;
        ind[6*i+5] = 4*i;
        i++;
    }   // for i=1; i<QuadSet::get()

    m_mesh_buffer->append(new_v, n*4, ind, n*6);

    if(lap_color)
    {
        video::S3DVertex lap_v[4];
        irr::u16         lap_ind[6];
        video::SColor     c(128, 255, 0, 0);
        m_all_nodes[0]->getQuad().getVertices(lap_v, *lap_color);

        // Now scale the length (distance between vertix 0 and 3
        // and between 1 and 2) to be 'length':
        Vec3 bb_min, bb_max;
        QuadSet::get()->getBoundingBox(&bb_min, &bb_max);
        // Length of the lap line about 3% of the 'height'
        // of the track.
        const float length=(bb_max.getZ()-bb_min.getZ())*0.03f;

        core::vector3df dl = lap_v[3].Pos-lap_v[0].Pos;
        float ll2 = dl.getLengthSQ();
        if(ll2<0.001)
            lap_v[3].Pos = lap_v[0].Pos+core::vector3df(0, 0, 1);
        else
            lap_v[3].Pos = lap_v[0].Pos+dl*length/sqrt(ll2);

        core::vector3df dr = lap_v[2].Pos-lap_v[1].Pos;
        float lr2 = dr.getLengthSQ();
        if(lr2<0.001)
            lap_v[2].Pos = lap_v[1].Pos+core::vector3df(0, 0, 1);
        else
            lap_v[2].Pos = lap_v[1].Pos+dr*length/sqrt(lr2);
        lap_ind[0] = 2;
        lap_ind[1] = 1;
        lap_ind[2] = 0;
        lap_ind[3] = 3;
        lap_ind[4] = 2;
        lap_ind[5] = 0;
        // Set it a bit higher to avoid issued with z fighting,
        // i.e. part of the lap line might not be visible.
        for(unsigned int i=0; i<4; i++)
            lap_v[i].Pos.Y += 0.1f;
#ifndef USE_TEXTURED_LINE
        m_mesh_buffer->append(lap_v, 4, lap_ind, 6);
#else
        lap_v[0].TCoords = core::vector2df(0,0);
        lap_v[1].TCoords = core::vector2df(3,0);
        lap_v[2].TCoords = core::vector2df(3,1);
        lap_v[3].TCoords = core::vector2df(0,1);
        m_mesh_buffer->append(lap_v, 4, lap_ind, 6);
        video::SMaterial &m = m_mesh_buffer->getMaterial();
        video::ITexture *t = irr_driver->getTexture("chess.png");
        m.setTexture(0, t);
#endif
    }

    // Instead of setting the bounding boxes, we could just disable culling,
    // since the debug track should always be drawn.
    //m_node->setAutomaticCulling(scene::EAC_OFF);
    m_mesh_buffer->recalculateBoundingBox();
    m_mesh->setBoundingBox(m_mesh_buffer->getBoundingBox());

    m_mesh_buffer->getMaterial().setTexture(0, irr_driver->getTexture("unlit.png"));

    delete[] ind;
    delete[] new_v;
}   // createMesh
Ejemplo n.º 6
0
/** Creates the slip stream object using a moving texture.
 *  \param kart Pointer to the kart to which the slip stream
 *              belongs to.
 */
SlipStream::SlipStream(AbstractKart* kart) : MovingTexture(0, 0), m_kart(kart)
{
    Material *material = material_manager->getMaterial("slipstream.png");

    createMesh(material);
    m_node = irr_driver->addMesh(m_mesh, "splistream");

    scene::IMeshBuffer* buffer = m_mesh->getMeshBuffer(0);
    material->setMaterialProperties(&buffer->getMaterial(), buffer);

    STKMeshSceneNode* stk_node = dynamic_cast<STKMeshSceneNode*>(m_node);
    if (stk_node != NULL)
        stk_node->setReloadEachFrame(true);
    m_mesh->drop();

#ifdef DEBUG
    std::string debug_name = m_kart->getIdent()+" (slip-stream)";
    m_node->setName(debug_name.c_str());
#endif
    m_node->setPosition(core::vector3df(0,
                                        0*0.25f+2.5,
                                        m_kart->getKartLength()) );
    m_node->setVisible(false);
    setTextureMatrix(&(m_node->getMaterial(0).getTextureMatrix(0)));
    m_slipstream_time      = 0.0f;

    float length = m_kart->getKartProperties()->getSlipstreamLength() *
                   m_kart->getPlayerDifficulty()->getSlipstreamLength();
    float kw     = m_kart->getKartWidth();
    float ew     = m_kart->getKartProperties()->getSlipstreamWidth() *
                   m_kart->getPlayerDifficulty()->getSlipstreamWidth();
    float kl     = m_kart->getKartLength();

    Vec3 p[4];
    p[0]=Vec3(-kw*0.5f, 0, -kl*0.5f       );
    p[1]=Vec3(-ew*0.5f, 0, -kl*0.5f-length);
    p[2]=Vec3( ew*0.5f, 0, -kl*0.5f-length);
    p[3]=Vec3( kw*0.5f, 0, -kl*0.5f       );
    m_slipstream_original_quad = new Quad(p[0], p[1], p[2], p[3]);
    m_slipstream_quad          = new Quad(p[0], p[1], p[2], p[3]);
    if(UserConfigParams::m_slipstream_debug)
    {
        video::SMaterial material;
        material.MaterialType    = video::EMT_TRANSPARENT_ADD_COLOR;
        material.setFlag(video::EMF_BACK_FACE_CULLING, false);
        material.setFlag(video::EMF_LIGHTING, false);

        m_debug_mesh = irr_driver->createQuadMesh(&material, true);
        scene::IMeshBuffer *buffer = m_debug_mesh->getMeshBuffer(0);
        assert(buffer->getVertexType()==video::EVT_STANDARD);
        irr::video::S3DVertex* vertices
            = (video::S3DVertex*)buffer->getVertices();
        video::SColor red(128, 255, 0, 0);
        for(unsigned int i=0; i<4; i++)
        {
            vertices[i].Pos   = p[i].toIrrVector();
            vertices[i].Color = red;
            vertices[i].TCoords = core::vector2df(0, 0);
        }
        video::SMaterial &mat = buffer->getMaterial();
        // Meshes need a texture, otherwise stk crashes.
        video::ITexture *red_texture = getUnicolorTexture(red);
        mat.setTexture(0, red_texture);

        buffer->recalculateBoundingBox();
        m_mesh->setBoundingBox(buffer->getBoundingBox());
        m_debug_node = irr_driver->addMesh(m_debug_mesh, "splistream_debug", m_kart->getNode());
        m_debug_node->grab();
    }
    else
    {
        m_debug_mesh = NULL;
        m_debug_node = NULL;
    }

}   // SlipStream
Ejemplo n.º 7
0
/** Loads the kart properties from a file.
 *  \param filename Filename to load.
 *  \param node Name of the xml node to load the data from
 */
void KartProperties::load(const std::string &filename, const std::string &node)
{
    // Get the default values from STKConfig. This will also allocate any
    // pointers used in KartProperties

    const XMLNode* root = new XMLNode(filename);
    std::string kart_type;

    if (root->get("type", &kart_type))
    {
        // Handle the case that kart_type might be incorrect
        try
        {
            copyFrom(&stk_config->getKartProperties(kart_type));
        }
        catch (std::out_of_range)
        {
            copyFrom(&stk_config->getDefaultKartProperties());
        }   // try .. catch
    }
    else
        copyFrom(&stk_config->getDefaultKartProperties());

    // m_kart_model must be initialised after assigning the default
    // values from stk_config (otherwise all kart_properties will
    // share the same KartModel
    m_kart_model.reset(new KartModel(/*is_master*/true));

    m_root  = StringUtils::getPath(filename)+"/";
    m_ident = StringUtils::getBasename(StringUtils::getPath(filename));
    // If this is an addon kart, add "addon_" to the identifier - just in
    // case that an addon kart has the same directory name (and therefore
    // identifier) as an included kart.
    if(Addon::isAddon(filename))
        m_ident = Addon::createAddonId(m_ident);
    try
    {
        if(!root || root->getName()!="kart")
        {
            std::ostringstream msg;
            msg << "Couldn't load kart properties '" << filename <<
                "': no kart node.";

            throw std::runtime_error(msg.str());
        }
        getAllData(root);
        m_characteristic.reset(new XmlCharacteristic(root));
        combineCharacteristics();
    }
    catch(std::exception& err)
    {
        Log::error("[KartProperties]", "Error while parsing KartProperties '%s':",
                   filename.c_str());
        Log::error("[KartProperties]", "%s", err.what());
    }
    if(root) delete root;

    // Set a default group (that has to happen after init_default and load)
    if(m_groups.size()==0)
        m_groups.push_back(DEFAULT_GROUP_NAME);


    // Load material
    std::string materials_file = m_root+"materials.xml";
    file_manager->pushModelSearchPath  (m_root);
    file_manager->pushTextureSearchPath(m_root);

    irr_driver->setTextureErrorMessage("Error while loading kart '%s':",
                                       m_name);

    // addShared makes sure that these textures/material infos stay in memory
    material_manager->addSharedMaterial(materials_file);

    m_icon_file = m_root+m_icon_file;

    // Make permanent is important, since otherwise icons can get deleted
    // (e.g. when freeing temp. materials from a track, the last icon
    //  would get deleted, too.
    m_icon_material = material_manager->getMaterial(m_icon_file,
                                                    /*is_full_path*/true,
                                                    /*make_permanent*/true,
                                                    /*complain_if_not_found*/true,
                                                    /*strip_path*/false);
    if(m_minimap_icon_file!="")
        m_minimap_icon = irr_driver->getTexture(m_root+m_minimap_icon_file);
    else
        m_minimap_icon = NULL;

    if (m_minimap_icon == NULL)
    {
        m_minimap_icon = getUnicolorTexture(m_color);
    }

    // Only load the model if the .kart file has the appropriate version,
    // otherwise warnings are printed.
    if (m_version >= 1)
    {
        const bool success = m_kart_model->loadModels(*this);
        if (!success)
        {
            file_manager->popTextureSearchPath();
            file_manager->popModelSearchPath();
            throw std::runtime_error("Cannot load kart models");
        }
    }

    if(m_gravity_center_shift.getX()==UNDEFINED)
    {
        m_gravity_center_shift.setX(0);
        // Default: center at the very bottom of the kart.
        // If the kart is 'too high', its height will be changed in
        // kart.cpp, the same adjustment needs to be made here.
        if (m_kart_model->getHeight() > m_kart_model->getLength()*0.6f)
            m_gravity_center_shift.setY(m_kart_model->getLength()*0.6f*0.5f);
        else
            m_gravity_center_shift.setY(m_kart_model->getHeight()*0.5f);

        m_gravity_center_shift.setZ(0);
    }

    // In older STK versions the physical wheels where moved 'wheel_radius'
    // into the physical body (i.e. 'hypothetical' wheel shape would not
    // poke out of the physical shape). In order to make the karts a bit more
    // stable, the physical wheel position (i.e. location of raycast) were
    // moved to be on the corner of the shape. In order to retain the same
    // steering behaviour, the wheel base (which in turn determines the
    // turn angle at certain speeds) is shortened by 2*wheel_radius
    // Wheel radius was always 0.25, and is now not used anymore, but in order
    // to keep existing steering behaviour, the same formula is still
    // used.
    m_wheel_base = fabsf(m_kart_model->getLength() - 2*0.25f);

    m_shadow_texture = irr_driver->getTexture(m_shadow_file);

    irr_driver->unsetTextureErrorMessage();
    file_manager->popTextureSearchPath();
    file_manager->popModelSearchPath();

}   // load
Ejemplo n.º 8
0
/** Loads the kart properties from a file.
 *  \param filename Filename to load.
 *  \param node Name of the xml node to load the data from
 */
void KartProperties::load(const std::string &filename, const std::string &node)
{
    // Get the default values from STKConfig. This will also allocate any
    // pointers used in KartProperties

    const XMLNode* root = new XMLNode(filename);
    std::string kart_type;
    if (root->get("type", &kart_type))
        copyFrom(&stk_config->getKartProperties(kart_type));
    else
        copyFrom(&stk_config->getDefaultKartProperties());

    // m_kart_model must be initialised after assigning the default
    // values from stk_config (otherwise all kart_properties will
    // share the same KartModel
    m_kart_model  = new KartModel(/*is_master*/true);

    m_root  = StringUtils::getPath(filename)+"/";
    m_ident = StringUtils::getBasename(StringUtils::getPath(filename));
    // If this is an addon kart, add "addon_" to the identifier - just in
    // case that an addon kart has the same directory name (and therefore
    // identifier) as an included kart.
    if(Addon::isAddon(filename))
        m_ident = Addon::createAddonId(m_ident);
    try
    {
        if(!root || root->getName()!="kart")
        {
            std::ostringstream msg;
            msg << "Couldn't load kart properties '" << filename <<
                "': no kart node.";

            delete m_kart_model;
            throw std::runtime_error(msg.str());
        }
        getAllData(root);
    }
    catch(std::exception& err)
    {
        Log::error("[KartProperties]", "Error while parsing KartProperties '%s':",
                   filename.c_str());
        Log::error("[KartProperties]", "%s", err.what());
    }
    if(root) delete root;

    // Set a default group (that has to happen after init_default and load)
    if(m_groups.size()==0)
        m_groups.push_back(DEFAULT_GROUP_NAME);


    // Load material
    std::string materials_file = m_root+"materials.xml";
    file_manager->pushModelSearchPath  (m_root);
    file_manager->pushTextureSearchPath(m_root);

    irr_driver->setTextureErrorMessage("Error while loading kart '%s':",
                                       m_name);

    // addShared makes sure that these textures/material infos stay in memory
    material_manager->addSharedMaterial(materials_file);

    m_icon_file = m_root+m_icon_file;

    // Make permanent is important, since otherwise icons can get deleted
    // (e.g. when freeing temp. materials from a track, the last icon
    //  would get deleted, too.
    m_icon_material = material_manager->getMaterial(m_icon_file,
                                                    /*is_full_path*/true,
                                                    /*make_permanent*/true,
                                                    /*complain_if_not_found*/true,
                                                    /*strip_path*/false);
    if(m_minimap_icon_file!="")
        m_minimap_icon = irr_driver->getTexture(m_root+m_minimap_icon_file);
    else
        m_minimap_icon = NULL;

    if (m_minimap_icon == NULL)
    {
        m_minimap_icon = getUnicolorTexture(m_color);
    }

    // Only load the model if the .kart file has the appropriate version,
    // otherwise warnings are printed.
    if (m_version >= 1)
    {
        const bool success = m_kart_model->loadModels(*this);
        if (!success)
        {
            delete m_kart_model;
            file_manager->popTextureSearchPath();
            file_manager->popModelSearchPath();
            throw std::runtime_error("Cannot load kart models");
        }
    }

    if(m_gravity_center_shift.getX()==UNDEFINED)
    {
        m_gravity_center_shift.setX(0);
        // Default: center at the very bottom of the kart.
        m_gravity_center_shift.setY(m_kart_model->getHeight()*0.5f);
        m_gravity_center_shift.setZ(0);
    }
    m_kart_model->setDefaultPhysicsPosition(m_gravity_center_shift,
                                           m_wheel_radius           );
    m_wheel_base = fabsf( m_kart_model->getWheelPhysicsPosition(0).getZ()
                         -m_kart_model->getWheelPhysicsPosition(2).getZ());

    // Now convert the turn radius into turn angle:
    for(unsigned int i=0; i<m_turn_angle_at_speed.size(); i++)
    {
        m_turn_angle_at_speed.setY( i,
                            sin(m_wheel_base/m_turn_angle_at_speed.getY(i)) );
    }

    m_shadow_texture = irr_driver->getTexture(m_shadow_file);

    irr_driver->unsetTextureErrorMessage();
    file_manager->popTextureSearchPath();
    file_manager->popModelSearchPath();

}   // load