/** * @brief Set the position of the point that is being dragged. * Calling this function will also automatically set m_dragging to true, * which applyDragValue() have to be called to m_dragging. * @param the time(x position) of the point being dragged * @param the value(y position) of the point being dragged * @param true to snip x position * @return */ MidiTime AutomationPattern::setDragValue( const MidiTime & time, const float value, const bool quantPos, const bool controlKey ) { if( m_dragging == false ) { MidiTime newTime = quantPos ? Note::quantized( time, quantization() ) : time; this->removeValue( newTime ); m_oldTimeMap = m_timeMap; m_dragging = true; } //Restore to the state before it the point were being dragged m_timeMap = m_oldTimeMap; for( timeMap::const_iterator it = m_timeMap.begin(); it != m_timeMap.end(); ++it ) { generateTangents( it, 3 ); } return this->putValue( time, value, quantPos, controlKey ); }
void AutomationPattern::flipY( int min, int max ) { timeMap tempMap = m_timeMap; timeMap::ConstIterator iterate = m_timeMap.lowerBound(0); float tempValue = 0; int numPoints = 0; for( int i = 0; ( iterate + i + 1 ) != m_timeMap.end() && ( iterate + i ) != m_timeMap.end() ; i++) { numPoints++; } for( int i = 0; i <= numPoints; i++ ) { if ( min < 0 ) { tempValue = valueAt( ( iterate + i ).key() ) * -1; putValue( MidiTime( (iterate + i).key() ) , tempValue, false); } else { tempValue = max - valueAt( ( iterate + i ).key() ); putValue( MidiTime( (iterate + i).key() ) , tempValue, false); } } generateTangents(); emit dataChanged(); }
/** * @brief Set the position of the point that is being draged. * Calling this function will also automatically set m_dragging to true, * which applyDragValue() have to be called to m_dragging. * @param the time(x position) of the point being dragged * @param the value(y position) of the point being dragged * @param true to snip x position * @return */ MidiTime AutomationPattern::setDragValue( const MidiTime & _time, const float _value, const bool _quant_pos ) { if( m_dragging == false ) { MidiTime newTime = _quant_pos && engine::automationEditor() ? note::quantized( _time, engine::automationEditor()->quantization() ) : _time; this->removeValue( newTime ); m_oldTimeMap = m_timeMap; m_dragging = true; } //Restore to the state before it the point were being dragged m_timeMap = m_oldTimeMap; for( timeMap::const_iterator it = m_timeMap.begin(); it != m_timeMap.end(); it++ ) { generateTangents(it, 3); } return this->putValue( _time, _value, _quant_pos ); }
MidiTime AutomationPattern::putValue( const MidiTime & _time, const float _value, const bool _quant_pos ) { cleanObjects(); MidiTime newTime = _quant_pos && engine::automationEditor() ? note::quantized( _time, engine::automationEditor()->quantization() ) : _time; m_timeMap[newTime] = _value; timeMap::const_iterator it = m_timeMap.find( newTime ); if( it != m_timeMap.begin() ) { it--; } generateTangents(it, 3); // we need to maximize our length in case we're part of a hidden // automation track as the user can't resize this pattern if( getTrack() && getTrack()->type() == track::HiddenAutomationTrack ) { changeLength( length() ); } emit dataChanged(); return newTime; }
void AutomationPattern::removeValue( const MidiTime & _time, const bool _quant_pos ) { cleanObjects(); MidiTime newTime = _quant_pos && engine::automationEditor() ? note::quantized( _time, engine::automationEditor()->quantization() ) : _time; m_timeMap.remove( newTime ); m_tangents.remove( newTime ); timeMap::const_iterator it = m_timeMap.lowerBound( newTime ); if( it != m_timeMap.begin() ) { it--; } generateTangents(it, 3); if( getTrack() && getTrack()->type() == track::HiddenAutomationTrack ) { changeLength( length() ); } emit dataChanged(); }
Entity::Entity(string name, string fileName){ this->name = name; this->fileName = fileName; vertices = NULL; indices = NULL; normals = NULL; vboVertices = NULL; vboIndices = NULL; vboNormals = NULL; shaderProgram = NULL ; vertexShader = NULL ; fragmentShader = NULL ; textureCoordinates = NULL; tangents = NULL; readMeshXml(); readMaterial(); generateTangents(); createVBOs(); transformation.setToIdentity() ; live = true; }
void AutomationPattern::loadSettings( const QDomElement & _this ) { clear(); movePosition( _this.attribute( "pos" ).toInt() ); setName( _this.attribute( "name" ) ); setProgressionType( static_cast<ProgressionTypes>( _this.attribute( "prog" ).toInt() ) ); setTension( _this.attribute( "tens" ) ); setMuted(_this.attribute( "mute", QString::number( false ) ).toInt() ); for( QDomNode node = _this.firstChild(); !node.isNull(); node = node.nextSibling() ) { QDomElement element = node.toElement(); if( element.isNull() ) { continue; } if( element.tagName() == "time" ) { m_timeMap[element.attribute( "pos" ).toInt()] = element.attribute( "value" ).toFloat(); } else if( element.tagName() == "object" ) { m_idsToResolve << element.attribute( "id" ).toInt(); } } int len = _this.attribute( "len" ).toInt(); if( len <= 0 ) { // TODO: Handle with an upgrade method updateLength(); } else { changeLength( len ); } generateTangents(); }
void AutomationPattern::removeValue( const MidiTime & time ) { cleanObjects(); m_timeMap.remove( time ); m_tangents.remove( time ); timeMap::const_iterator it = m_timeMap.lowerBound( time ); if( it != m_timeMap.begin() ) { --it; } generateTangents(it, 3); if( getTrack() && getTrack()->type() == Track::HiddenAutomationTrack ) { updateLength(); } emit dataChanged(); }
MidiTime AutomationPattern::putValue( const MidiTime & time, const float value, const bool quantPos, const bool ignoreSurroundingPoints ) { cleanObjects(); MidiTime newTime = quantPos ? Note::quantized( time, quantization() ) : time; m_timeMap[ newTime ] = value; timeMap::const_iterator it = m_timeMap.find( newTime ); // Remove control points that are covered by the new points // quantization value. Control Key to override if( ! ignoreSurroundingPoints ) { for( int i = newTime + 1; i < newTime + quantization(); ++i ) { AutomationPattern::removeValue( i ); } } if( it != m_timeMap.begin() ) { --it; } generateTangents( it, 3 ); // we need to maximize our length in case we're part of a hidden // automation track as the user can't resize this pattern if( getTrack() && getTrack()->type() == Track::HiddenAutomationTrack ) { updateLength(); } emit dataChanged(); return newTime; }
void DrawVBOMesh::loadOBJ(Geometry::MeshDataStructPtr obj) { m_objInfo = obj; Geometry::MeshDataStruct& refObjInfo = *m_objInfo; std::vector< vrGLMVec3 > & points = refObjInfo.points; std::vector< vrGLMVec3 > & normals = refObjInfo.normals; std::vector< vrGLMVec2 > & texCoords = refObjInfo.texCoords; std::vector<vrInt>& faces = refObjInfo.faces; if (normals.size() == 0) { generateAveragedNormals(points, normals, faces); } std::vector< vrGLMVec4 > tangents; if (genTang && texCoords.size() > 0) { generateTangents(points, normals, faces, texCoords, tangents); } if (reCenterMesh) { center(points); } storeVBO(points, normals, texCoords, tangents, faces); }
void AutomationPattern::flipX( int length ) { timeMap tempMap; timeMap::ConstIterator iterate = m_timeMap.lowerBound(0); float tempValue = 0; int numPoints = 0; for( int i = 0; ( iterate + i + 1 ) != m_timeMap.end() && ( iterate + i ) != m_timeMap.end() ; i++) { numPoints++; } float realLength = ( iterate + numPoints ).key(); if ( length != -1 && length != realLength) { if ( realLength < length ) { tempValue = valueAt( ( iterate + numPoints ).key() ); putValue( MidiTime( length ) , tempValue, false); numPoints++; for( int i = 0; i <= numPoints; i++ ) { tempValue = valueAt( ( iterate + i ).key() ); MidiTime newTime = MidiTime( length - ( iterate + i ).key() ); tempMap[newTime] = tempValue; } } else { for( int i = 0; i <= numPoints; i++ ) { tempValue = valueAt( ( iterate + i ).key() ); MidiTime newTime; if ( ( iterate + i ).key() <= length ) { newTime = MidiTime( length - ( iterate + i ).key() ); } else { newTime = MidiTime( ( iterate + i ).key() ); } tempMap[newTime] = tempValue; } } } else { for( int i = 0; i <= numPoints; i++ ) { tempValue = valueAt( ( iterate + i ).key() ); cleanObjects(); MidiTime newTime = MidiTime( realLength - ( iterate + i ).key() ); tempMap[newTime] = tempValue; } } m_timeMap.clear(); m_timeMap = tempMap; generateTangents(); emit dataChanged(); }
Cone::Cone(std::string name, int id, int tesselation, float3 color, float radius, float height) : Primitive(0, name, id, tesselation, color), radius_(radius), height_(height) { hasVBO_[NORMALS] = true; hasVBO_[COLORS] = true; hasVBO_[TEXCOORDS] = true; int steps = 6 + tesselation_ * tesselation_; for (int i = 0; i < steps; i++) { double phi_left = 2 * M_PI * i / static_cast<double>(steps); double phi_right = 2 * M_PI * (i + 1) / static_cast<double>(steps); // cap float3 position0 = float3(radius_ * sin(phi_left), 0, radius_ * cos(phi_left)); float3 position1 = float3(radius_ * sin(phi_right), 0, radius_ * cos(phi_right)); float3 position2 = float3(0, 0, 0); vertexPositions_.push_back(position0); vertexPositions_.push_back(position1); vertexPositions_.push_back(position2); vertexNormals_.push_back(float3(0, -1, 0)); vertexNormals_.push_back(float3(0, -1, 0)); vertexNormals_.push_back(float3(0, -1, 0)); float3 texCoord0 = float3(position0.x_ / 2 + 0.5, position0.z_ / 2 + 0.5, 0.0f); float3 texCoord1 = float3(position1.x_ / 2 + 0.5, position1.z_ / 2 + 0.5, 0.0f); float3 texCoord2 = float3(position2.x_ / 2 + 0.5, position2.z_ / 2 + 0.5, 0.0f); vertexTextureCoordinates_.push_back(texCoord0); vertexTextureCoordinates_.push_back(texCoord1); vertexTextureCoordinates_.push_back(texCoord2); // body position0 = float3(radius_ * sin(phi_left), 0, radius_ * cos(phi_left)); position1 = float3(radius_ * sin(phi_right), 0, radius_ * cos(phi_right)); position2 = float3(0, height_, 0); vertexPositions_.push_back(position0); vertexPositions_.push_back(position1); vertexPositions_.push_back(position2); float degree = atan(height_ / radius_); float3 normal0 = float3(cos(degree) * 0 + (1 - cos(degree)) * radius_ * sin(phi_left), cos(degree) * 1 + (1 - cos(degree)) * 0, cos(degree) * 0 + (1 - cos(degree)) * radius_ * cos(phi_left)); vertexNormals_.push_back(normal0); float3 normal1 = float3(cos(degree) * 0 + (1 - cos(degree)) * radius_ * sin(phi_right), cos(degree) * 1 + (1 - cos(degree)) * 0, cos(degree) * 0 + (1 - cos(degree)) * radius_ * cos(phi_right)); vertexNormals_.push_back(normal1); float3 normal2 = float3(cos(degree) * 0 + (1 - cos(degree)) * (radius_ * sin(phi_left) + radius_ * sin(phi_right)) / 2, cos(degree) * 1 + (1 - cos(degree)) * 0, cos(degree) * 0 + (1 - cos(degree)) * (radius_ * cos(phi_left) + radius_ * cos(phi_right)) / 2); //normal2 = float3(0, 1, 0); vertexNormals_.push_back(normal2); vertexTextureCoordinates_.push_back(float3(i / (float) steps, 0, 0)); vertexTextureCoordinates_.push_back(float3((i + 1) / (float) steps, 0, 0)); vertexTextureCoordinates_.push_back(float3(((i + 0.5) / (float) steps) , 1, 0)); } // set indices list for (uint i = 0; i < vertexPositions_.size(); i++) { indicesList_.push_back(i); } float coneColor[3] = {1, 1, 0}; for (uint i = 0; i < vertexPositions_.size(); i++) { vertexColors_.push_back(float3(coneColor)); } generateTangents(3); }
bool ModelOBJ::import(const char *pszFilename, bool rebuildNormals) { FILE *pFile = fopen(pszFilename, "r"); if (!pFile) return false; // Extract the directory the OBJ file is in from the file name. // This directory path will be used to load the OBJ's associated MTL file. m_directoryPath.clear(); std::string filename = pszFilename; std::string::size_type offset = filename.find_last_of('\\'); if (offset != std::string::npos) { m_directoryPath = filename.substr(0, ++offset); } else { offset = filename.find_last_of('/'); if (offset != std::string::npos) m_directoryPath = filename.substr(0, ++offset); } // Import the OBJ file. importGeometryFirstPass(pFile); rewind(pFile); importGeometrySecondPass(pFile); fclose(pFile); // Perform post import tasks. buildMeshes(); bounds(m_center, m_width, m_height, m_length, m_radius); // Build vertex normals if required. if (rebuildNormals) { generateNormals(); } else { if (!hasNormals()) generateNormals(); } // Build tangents is required. for (int i = 0; i < m_numberOfMaterials; ++i) { if (!m_materials[i].bumpMapFilename.empty()) { generateTangents(); break; } } return true; }
void init() { GLfloat skyboxVertices[] = { // Positions -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f }; planet = tetrahedron(5); object = ObjLoadModel("include/obj/torus.obj"); vec3 tangent[planet.vertexNumber]; vec3 bitangent[planet.vertexNumber]; *tangent = *generateTangents(planet.vertexNumber, planet.points, tangent, bitangent); vec3 vna[planet.vertexNumber]; *vna = *generateSmoothNormals(planet.vertexNumber, vna, planet.points, planet.normals); createShader(&skyboxShader, "src/shaders/skybox.vert", "src/shaders/skybox.frag"); createShader(&sunShader, "src/shaders/sun.vert", "src/shaders/sun.frag"); createShader(&planetShader, "src/shaders/planet.vert", "src/shaders/planet.frag"); createShader(&atmosphereShader, "src/shaders/atmosphere.vert", "src/shaders/atmosphere.frag"); glGenFramebuffers(1, &hdrFBO); glGenTextures(1, &colorBuffer); glBindTexture(GL_TEXTURE_2D, colorBuffer); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, WIDTH, HEIGHT, 0, GL_RGB, GL_FLOAT, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glGenRenderbuffers(1, &rboDepth); glBindRenderbuffer(GL_RENDERBUFFER, rboDepth); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, WIDTH, HEIGHT); glBindFramebuffer(GL_FRAMEBUFFER, hdrFBO); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorBuffer, 0); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboDepth); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) printf("Framebuffer not complete!\n"); glBindFramebuffer(GL_FRAMEBUFFER, 0); glGenVertexArrays(1, &planetVAO); glBindVertexArray(planetVAO); glGenBuffers(1, &planetVBO); glBindBuffer(GL_ARRAY_BUFFER, planetVBO); glBufferData(GL_ARRAY_BUFFER, planet.size + planet.nsize, NULL, GL_STATIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, planet.size, planet.points); glBufferSubData(GL_ARRAY_BUFFER, planet.size, planet.nsize, vna); glBufferSubData(GL_ARRAY_BUFFER, planet.size+planet.nsize, sizeof(tangent), tangent); glBufferSubData(GL_ARRAY_BUFFER, planet.size+planet.nsize+sizeof(tangent), sizeof(tangent), tangent); vPosition = glGetAttribLocation(planetShader, "vPosition"); glEnableVertexAttribArray(vPosition); glVertexAttribPointer(vPosition, 3, GL_FLOAT, GL_FALSE, 3*sizeof(GLfloat), BUFFER_OFFSET(0)); vNormal = glGetAttribLocation(planetShader, "vNormal"); glEnableVertexAttribArray(vNormal); glVertexAttribPointer(vNormal, 3, GL_FLOAT, GL_FALSE, 3*sizeof(GLfloat), BUFFER_OFFSET(planet.size)); vTangent = glGetAttribLocation(planetShader, "vTangent"); glEnableVertexAttribArray(vTangent); glVertexAttribPointer(vTangent, 3, GL_FLOAT, GL_FALSE, 3*sizeof(GLfloat), BUFFER_OFFSET(planet.size+planet.nsize)); vBitangent = glGetAttribLocation(planetShader, "vBitangent"); glEnableVertexAttribArray(vBitangent); glVertexAttribPointer(vBitangent, 3, GL_FLOAT, GL_FALSE, 3*sizeof(GLfloat), BUFFER_OFFSET(planet.size+planet.nsize+sizeof(tangent))); glBindVertexArray(0); glGenVertexArrays(1, &skyboxVAO); glBindVertexArray(skyboxVAO); glGenBuffers(1, &skyboxVBO); glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO); glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), skyboxVertices, GL_STATIC_DRAW); vPosition = glGetAttribLocation(skyboxShader, "vPosition"); glEnableVertexAttribArray(vPosition); glVertexAttribPointer(vPosition, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0); glBindVertexArray(0); glGenVertexArrays(1, &objectVAO); glBindVertexArray(objectVAO); glGenBuffers(1, &objectVBO); glBindBuffer(GL_ARRAY_BUFFER, objectVBO); glBufferData(GL_ARRAY_BUFFER, object.size + object.nsize, NULL, GL_STATIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, object.size, object.points); glBufferSubData(GL_ARRAY_BUFFER, object.size, object.nsize, object.normals); vPosition = glGetAttribLocation(sunShader, "vPosition"); glEnableVertexAttribArray(vPosition); glVertexAttribPointer(vPosition, 3, GL_FLOAT, GL_FALSE, 3*sizeof(GLfloat), BUFFER_OFFSET(0)); vNormal = glGetAttribLocation(sunShader, "vNormal"); glEnableVertexAttribArray(vNormal); glVertexAttribPointer(vNormal, 3, GL_FLOAT, GL_FALSE, 3*sizeof(GLfloat), BUFFER_OFFSET(object.size)); glBindVertexArray(0); glGenVertexArrays(1, &atmosphereVAO); glBindVertexArray(atmosphereVAO); glGenBuffers(1, &atmosphereVBO); glBindBuffer(GL_ARRAY_BUFFER, atmosphereVBO); glBufferData(GL_ARRAY_BUFFER, planet.size + planet.nsize, NULL, GL_STATIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, planet.size, planet.points); glBufferSubData(GL_ARRAY_BUFFER, planet.size, planet.nsize, planet.normals); vPosition = glGetAttribLocation(atmosphereShader, "vPosition"); glEnableVertexAttribArray(vPosition); glVertexAttribPointer(vPosition, 3, GL_FLOAT, GL_FALSE, 3*sizeof(GLfloat), BUFFER_OFFSET(0)); vNormal = glGetAttribLocation(atmosphereShader, "vNormal"); glEnableVertexAttribArray(vNormal); glVertexAttribPointer(vNormal, 3, GL_FLOAT, GL_FALSE, 3*sizeof(GLfloat), BUFFER_OFFSET(planet.size)); glBindVertexArray(0); glEnable(GL_DEPTH_TEST); }
void AutomationPattern::generateTangents() { generateTangents(m_timeMap.begin(), m_timeMap.size()); }
bool ObjLoader::load(::QIODevice *ioDev, const QString &subMesh) { Q_CHECK_PTR(ioDev); if (!ioDev->isOpen()) { qCWarning(Render::Io) << "iodevice" << ioDev << "not open for reading"; return false; } int faceCount = 0; // Parse faces taking into account each vertex in a face can index different indices // for the positions, normals and texture coords; // Generate unique vertices (in OpenGL parlance) and output to m_points, m_texCoords, // m_normals and calculate mapping from faces to unique indices QVector<QVector3D> positions; QVector<QVector3D> normals; QVector<QVector2D> texCoords; QHash<FaceIndices, unsigned int> faceIndexMap; QVector<FaceIndices> faceIndexVector; bool skipping = false; int positionsOffset = 0; int normalsOffset = 0; int texCoordsOffset = 0; QRegExp subMeshMatch(subMesh); if (!subMeshMatch.isValid()) subMeshMatch.setPattern(QString(QStringLiteral("^(%1)$")).arg(subMesh)); Q_ASSERT(subMeshMatch.isValid()); QTextStream stream(ioDev); while (!stream.atEnd()) { QString line = stream.readLine(); line = line.simplified(); if (line.length() > 0 && line.at(0) != QChar::fromLatin1('#')) { const QVector<QStringRef> tokens = line.splitRef(QChar::fromLatin1(' ')); if (tokens.first() == QStringLiteral("v")) { if (tokens.size() < 4) { qCWarning(Render::Io) << "Unsupported number of components in vertex"; } else { if (!skipping) { float x = tokens.at(1).toFloat(); float y = tokens.at(2).toFloat(); float z = tokens.at(3).toFloat(); positions.append(QVector3D( x, y, z )); } else { positionsOffset++; } } } else if (tokens.first() == QStringLiteral("vt") && m_loadTextureCoords) { if (tokens.size() < 3) { qCWarning(Render::Io) << "Unsupported number of components in texture coordinate"; } else { if (!skipping) { // Process texture coordinate float s = tokens.at(1).toFloat(); float t = tokens.at(2).toFloat(); //FlipUVs t = 1.0f - t; texCoords.append(QVector2D( s, t )); } else { texCoordsOffset++; } } } else if (tokens.first() == QStringLiteral("vn")) { if (tokens.size() < 4) { qCWarning(Render::Io) << "Unsupported number of components in vertex normal"; } else { if (!skipping) { float x = tokens.at(1).toFloat(); float y = tokens.at(2).toFloat(); float z = tokens.at(3).toFloat(); normals.append(QVector3D( x, y, z )); } else { normalsOffset++; } } } else if (!skipping && tokens.first() == QStringLiteral("f")) { // Process face ++faceCount; int faceVertices = tokens.size() - 1; QVector<FaceIndices> face; face.reserve(faceVertices); for (int i = 0; i < faceVertices; i++) { FaceIndices faceIndices; const QVector<QStringRef> indices = tokens.at(i + 1).split(QChar::fromLatin1('/')); switch (indices.size()) { case 3: faceIndices.normalIndex = indices.at(2).toInt() - 1 - normalsOffset; // fall through case 2: faceIndices.texCoordIndex = indices.at(1).toInt() - 1 - texCoordsOffset; // fall through case 1: faceIndices.positionIndex = indices.at(0).toInt() - 1 - positionsOffset; break; default: qCWarning(Render::Io) << "Unsupported number of indices in face element"; } face.append(faceIndices); } // If number of edges in face is greater than 3, // decompose into triangles as a triangle fan. FaceIndices v0 = face[0]; FaceIndices v1 = face[1]; FaceIndices v2 = face[2]; // First face addFaceVertex(v0, faceIndexVector, faceIndexMap); addFaceVertex(v1, faceIndexVector, faceIndexMap); addFaceVertex(v2, faceIndexVector, faceIndexMap); for ( int i = 3; i < face.size(); ++i ) { v1 = v2; v2 = face[i]; addFaceVertex(v0, faceIndexVector, faceIndexMap); addFaceVertex(v1, faceIndexVector, faceIndexMap); addFaceVertex(v2, faceIndexVector, faceIndexMap); } // end of face } else if (tokens.first() == QStringLiteral("o")) { if (tokens.size() < 2) { qCWarning(Render::Io) << "Missing submesh name"; } else { if (!subMesh.isEmpty() ) { QString objName = tokens.at(1).toString(); skipping = subMeshMatch.indexIn(objName) < 0; } } } } // end of input line } // while (!stream.atEnd()) updateIndices(positions, normals, texCoords, faceIndexMap, faceIndexVector); if (m_normals.isEmpty()) generateAveragedNormals(m_points, m_normals, m_indices); if (m_generateTangents && !m_texCoords.isEmpty()) generateTangents(m_points, m_normals, m_indices, m_texCoords, m_tangents); if (m_centerMesh) center(m_points); qCDebug(Render::Io) << "Loaded mesh:"; qCDebug(Render::Io) << " " << m_points.size() << "points"; qCDebug(Render::Io) << " " << faceCount << "faces"; qCDebug(Render::Io) << " " << m_indices.size() / 3 << "triangles."; qCDebug(Render::Io) << " " << m_normals.size() << "normals"; qCDebug(Render::Io) << " " << m_tangents.size() << "tangents "; qCDebug(Render::Io) << " " << m_texCoords.size() << "texture coordinates."; return true; }
bool xmlMeshLoad(const char * filename, void * data) { MLOG_DEBUG("xmlMeshLoad " << filename?filename:"NULL"); MLevel * level = MEngine::getInstance()->getLevel(); // read document TiXmlDocument doc(filename); if(! doc.LoadFile()) { MLOG_WARNING("TiXmlDocument load failed : " << doc.ErrorDesc() << " line " << doc.ErrorRow()); return false; } TiXmlHandle hDoc(&doc); TiXmlElement * pRootNode; TiXmlHandle hRoot(0); // Maratis pRootNode = hDoc.FirstChildElement().Element(); if(! pRootNode) { MLOG_WARNING("Cannot find any root node"); return false; } if(strcmp(pRootNode->Value(), "Maratis") != 0) { MLOG_WARNING("Cannot find Maratis root node"); return false; } hRoot = TiXmlHandle(pRootNode); // Mesh TiXmlElement * pMeshNode = pRootNode->FirstChildElement("Mesh"); if(! pMeshNode) { MLOG_WARNING("Cannot find a Mesh node"); return false; } // create new mesh MMesh * mesh = (MMesh *)data; mesh->clear(); char path[256]; char meshRep[256]; char vertShadPath[256]; char fragShadPath[256]; // mesh rep getRepertory(meshRep, filename); // animation if(! loadAnim(pMeshNode, meshRep, mesh)) { // load external anim file (depracated) char animFilename[256]; strcpy(animFilename, filename); strcpy(animFilename + strlen(animFilename) - 4, "anim"); loadAnimFile(mesh, animFilename, meshRep); } // Textures TiXmlElement * texturesNode = pMeshNode->FirstChildElement("Textures"); if(texturesNode) { MLOG_DEBUG("entering Textures node"); unsigned int numTextures = 0; texturesNode->QueryUIntAttribute("num", &numTextures); mesh->allocTextures(numTextures); // Texture TiXmlElement * textureNode = texturesNode->FirstChildElement("Texture"); for(textureNode; textureNode; textureNode=textureNode->NextSiblingElement("Texture")) { const char * file = NULL; bool mipmap = true; // image TiXmlElement * imageNode = textureNode->FirstChildElement("image"); if(imageNode) { int value = 1; file = imageNode->Attribute("filename"); imageNode->QueryIntAttribute("mipmap", &value); mipmap = (value == 1); } if(! file) { mesh->addNewTexture(NULL); continue; } // load texture getGlobalFilename(path, meshRep, file); MTextureRef * texRef = level->loadTexture(path, mipmap); MTexture * texture = mesh->addNewTexture(texRef); // tile TiXmlElement * tileNode = textureNode->FirstChildElement("tile"); if(tileNode) { const char * uTile = tileNode->Attribute("u"); const char * vTile = tileNode->Attribute("v"); if(uTile){ if(strcmp(uTile, "clamp") == 0) texture->setUWrapMode(M_WRAP_CLAMP); else texture->setUWrapMode(M_WRAP_REPEAT); } if(vTile){ if(strcmp(vTile, "clamp") == 0) texture->setVWrapMode(M_WRAP_CLAMP); else texture->setVWrapMode(M_WRAP_REPEAT); } } // translate TiXmlElement * translateNode = textureNode->FirstChildElement("translate"); if(translateNode) { MVector2 translate = texture->getTexTranslate(); translateNode->QueryFloatAttribute("x", &translate.x); translateNode->QueryFloatAttribute("y", &translate.y); texture->setTexTranslate(translate); } // scale TiXmlElement * scaleNode = textureNode->FirstChildElement("scale"); if(scaleNode) { MVector2 scale = texture->getTexScale(); scaleNode->QueryFloatAttribute("x", &scale.x); scaleNode->QueryFloatAttribute("y", &scale.y); texture->setTexScale(scale); } // rotate TiXmlElement * rotateNode = textureNode->FirstChildElement("rotate"); if(rotateNode) { float angle = 0; rotateNode->QueryFloatAttribute("angle", &angle); texture->setTexRotate(angle); } } } // Materials TiXmlElement * materialsNode = pMeshNode->FirstChildElement("Materials"); if(materialsNode) { MLOG_DEBUG("entering Materials node"); unsigned int numMaterials = 0; materialsNode->QueryUIntAttribute("num", &numMaterials); mesh->allocMaterials(numMaterials); // Material TiXmlElement * materialNode = materialsNode->FirstChildElement("Material"); for(materialNode; materialNode; materialNode=materialNode->NextSiblingElement("Material")) { MMaterial * material = mesh->addNewMaterial(); int type = 0; materialNode->QueryIntAttribute("type", &type); material->setType(type); float opacity=1, shininess=0, customValue=0; MVector3 diffuseColor; MVector3 specularColor; MVector3 emitColor; MVector3 customColor; // blend int blendType = 0; TiXmlElement * blendNode = materialNode->FirstChildElement("blend"); if(blendNode) blendNode->QueryIntAttribute("type", &blendType); switch(blendType) { case 2: material->setBlendMode(M_BLENDING_ALPHA); break; case 3: material->setBlendMode(M_BLENDING_ADD); break; case 4: material->setBlendMode(M_BLENDING_PRODUCT); break; } // opacity TiXmlElement * opacityNode = materialNode->FirstChildElement("opacity"); if(opacityNode) opacityNode->QueryFloatAttribute("value", &opacity); // shininess TiXmlElement * shininessNode = materialNode->FirstChildElement("shininess"); if(shininessNode) shininessNode->QueryFloatAttribute("value", &shininess); // customValue TiXmlElement * customValueNode = materialNode->FirstChildElement("customValue"); if(customValueNode) customValueNode->QueryFloatAttribute("value", &customValue); material->setOpacity(opacity); material->setShininess(shininess); material->setCustomValue(customValue); // diffuseColor TiXmlElement * diffuseColorNode = materialNode->FirstChildElement("diffuseColor"); if(diffuseColorNode){ diffuseColorNode->QueryFloatAttribute("r", &diffuseColor.x); diffuseColorNode->QueryFloatAttribute("g", &diffuseColor.y); diffuseColorNode->QueryFloatAttribute("b", &diffuseColor.z); material->setDiffuse(diffuseColor); } // specularColor TiXmlElement * specularColorNode = materialNode->FirstChildElement("specularColor"); if(specularColorNode){ specularColorNode->QueryFloatAttribute("r", &specularColor.x); specularColorNode->QueryFloatAttribute("g", &specularColor.y); specularColorNode->QueryFloatAttribute("b", &specularColor.z); material->setSpecular(specularColor); } // emitColor TiXmlElement * emitColorNode = materialNode->FirstChildElement("emitColor"); if(emitColorNode){ emitColorNode->QueryFloatAttribute("r", &emitColor.x); emitColorNode->QueryFloatAttribute("g", &emitColor.y); emitColorNode->QueryFloatAttribute("b", &emitColor.z); material->setEmit(emitColor); } // customColor TiXmlElement * customColorNode = materialNode->FirstChildElement("customColor"); if(customColorNode){ customColorNode->QueryFloatAttribute("r", &customColor.x); customColorNode->QueryFloatAttribute("g", &customColor.y); customColorNode->QueryFloatAttribute("b", &customColor.z); material->setCustomColor(customColor); } // TexturesPass TiXmlElement * texturesPassNode = materialNode->FirstChildElement("TexturesPass"); if(texturesPassNode) { unsigned int numTexturesPass = 0; texturesPassNode->QueryUIntAttribute("num", &numTexturesPass); material->allocTexturesPass(numTexturesPass); // texturePass TiXmlElement * texturePassNode = texturesPassNode->FirstChildElement("texturePass"); for(texturePassNode; texturePassNode; texturePassNode=texturePassNode->NextSiblingElement("texturePass")) { int textureId = -1; unsigned int mapChannel = 0; const char * mode = texturePassNode->Attribute("mode"); texturePassNode->QueryIntAttribute("texture", &textureId); if(textureId < 0) { material->addTexturePass(NULL, M_TEX_COMBINE_MODULATE, 0); continue; } texturePassNode->QueryUIntAttribute("mapChannel", &mapChannel); // combine mode M_TEX_COMBINE_MODES texCombine = M_TEX_COMBINE_MODULATE; if(strcmp(mode, "modulate") == 0) texCombine = M_TEX_COMBINE_MODULATE; else if(strcmp(mode, "replace") == 0) texCombine = M_TEX_COMBINE_REPLACE; else if(strcmp(mode, "alpha") == 0) texCombine = M_TEX_COMBINE_ALPHA; else if(strcmp(mode, "dot") == 0) texCombine = M_TEX_COMBINE_DOT; else if(strcmp(mode, "add") == 0) texCombine = M_TEX_COMBINE_ADD; else if(strcmp(mode, "sub") == 0) texCombine = M_TEX_COMBINE_SUB; // add texture pass material->addTexturePass(mesh->getTexture(textureId), texCombine, mapChannel); } } // FX { // vertexShader const char * vertShadFile = NULL; TiXmlElement * vertexShaderNode = materialNode->FirstChildElement("vertexShader"); if(vertexShaderNode){ vertShadFile = vertexShaderNode->Attribute("file"); } // fragmentShader const char * fragShadFile = NULL; TiXmlElement * fragmentShaderNode = materialNode->FirstChildElement("fragmentShader"); if(fragmentShaderNode){ fragShadFile = fragmentShaderNode->Attribute("file"); } // create FX if(vertShadFile && fragShadFile) { getGlobalFilename(vertShadPath, meshRep, vertShadFile); getGlobalFilename(fragShadPath, meshRep, fragShadFile); MShaderRef * vertShad = level->loadShader(vertShadPath, M_SHADER_VERTEX); MShaderRef * pixShad = level->loadShader(fragShadPath, M_SHADER_PIXEL); if(vertShad && pixShad) { MFXRef * FXRef = level->createFX(vertShad, pixShad); material->setFXRef(FXRef); } } } // ZFX (optional optim) { // ZVertexShader const char * vertShadFile = NULL; TiXmlElement * vertexShaderNode = materialNode->FirstChildElement("ZVertexShader"); if(vertexShaderNode){ vertShadFile = vertexShaderNode->Attribute("file"); } // ZFragmentShader const char * fragShadFile = NULL; TiXmlElement * fragmentShaderNode = materialNode->FirstChildElement("ZFragmentShader"); if(fragmentShaderNode){ fragShadFile = fragmentShaderNode->Attribute("file"); } // create ZFX if(vertShadFile && fragShadFile) { getGlobalFilename(vertShadPath, meshRep, vertShadFile); getGlobalFilename(fragShadPath, meshRep, fragShadFile); MShaderRef * vertShad = level->loadShader(vertShadPath, M_SHADER_VERTEX); MShaderRef * pixShad = level->loadShader(fragShadPath, M_SHADER_PIXEL); if(vertShad && pixShad) { MFXRef * ZFXRef = level->createFX(vertShad, pixShad); material->setZFXRef(ZFXRef); } } } } } // Bones TiXmlElement * bonesNode = pMeshNode->FirstChildElement("Bones"); if(bonesNode) { MLOG_DEBUG("entering Bones node"); MArmature * armature = mesh->createArmature(); unsigned int b, numBones = 0; bonesNode->QueryUIntAttribute("num", &numBones); armature->allocBones(numBones); // add bones for(b=0; b<numBones; b++) armature->addNewBone(); b = 0; // Bone TiXmlElement * boneNode = bonesNode->FirstChildElement("Bone"); for(boneNode; boneNode; boneNode=boneNode->NextSiblingElement("Bone")) { if(b >= armature->getBonesNumber()) break; MOBone * bone = armature->getBone(b); const char * name = boneNode->Attribute("name"); if(name) bone->setName(name); // parent TiXmlElement * parentNode = boneNode->FirstChildElement("parent"); if(parentNode){ unsigned int boneId = 0; parentNode->QueryUIntAttribute("id", &boneId); bone->linkTo(armature->getBone(boneId)); } // position TiXmlElement * positionNode = boneNode->FirstChildElement("position"); if(positionNode){ MVector3 position; positionNode->QueryFloatAttribute("x", &position.x); positionNode->QueryFloatAttribute("y", &position.y); positionNode->QueryFloatAttribute("z", &position.z); bone->setPosition(position); } // rotation TiXmlElement * rotationNode = boneNode->FirstChildElement("rotation"); if(rotationNode){ MVector3 euler; rotationNode->QueryFloatAttribute("x", &euler.x); rotationNode->QueryFloatAttribute("y", &euler.y); rotationNode->QueryFloatAttribute("z", &euler.z); bone->setEulerRotation(euler); } // scale TiXmlElement * scaleNode = boneNode->FirstChildElement("scale"); if(scaleNode){ MVector3 scale; scaleNode->QueryFloatAttribute("x", &scale.x); scaleNode->QueryFloatAttribute("y", &scale.y); scaleNode->QueryFloatAttribute("z", &scale.z); bone->setScale(scale); } b++; } // construct bones inverse pose matrix armature->constructBonesInversePoseMatrix(); } // SubMeshs TiXmlElement * subMeshsNode = pMeshNode->FirstChildElement("SubMeshs"); if(! subMeshsNode) return true; unsigned int numSubMeshs = 0; subMeshsNode->QueryUIntAttribute("num", &numSubMeshs); if(numSubMeshs == 0) return true; // alloc subMeshs MSubMesh * subMeshs = mesh->allocSubMeshs(numSubMeshs); // BoundingBox TiXmlElement * boundingBoxNode = pMeshNode->FirstChildElement("BoundingBox"); if(boundingBoxNode) { MVector3 * min = &mesh->getBoundingBox()->min; MVector3 * max = &mesh->getBoundingBox()->max; boundingBoxNode->QueryFloatAttribute("minx", &min->x); boundingBoxNode->QueryFloatAttribute("miny", &min->y); boundingBoxNode->QueryFloatAttribute("minz", &min->z); boundingBoxNode->QueryFloatAttribute("maxx", &max->x); boundingBoxNode->QueryFloatAttribute("maxy", &max->y); boundingBoxNode->QueryFloatAttribute("maxz", &max->z); } // SubMesh TiXmlElement * SubMeshNode = subMeshsNode->FirstChildElement("SubMesh"); for(SubMeshNode; SubMeshNode; SubMeshNode=SubMeshNode->NextSiblingElement("SubMesh")) { MSubMesh * subMesh = subMeshs; // BoundingBox boundingBoxNode = SubMeshNode->FirstChildElement("BoundingBox"); if(boundingBoxNode) { MVector3 * min = &subMesh->getBoundingBox()->min; MVector3 * max = &subMesh->getBoundingBox()->max; boundingBoxNode->QueryFloatAttribute("minx", &min->x); boundingBoxNode->QueryFloatAttribute("miny", &min->y); boundingBoxNode->QueryFloatAttribute("minz", &min->z); boundingBoxNode->QueryFloatAttribute("maxx", &max->x); boundingBoxNode->QueryFloatAttribute("maxy", &max->y); boundingBoxNode->QueryFloatAttribute("maxz", &max->z); } // Vertices TiXmlElement * verticesNode = SubMeshNode->FirstChildElement("Vertices"); if(verticesNode) { unsigned int numVertices = 0; verticesNode->QueryUIntAttribute("num", &numVertices); MVector3 * vertices = subMesh->allocVertices(numVertices); // vertex TiXmlElement * vertexNode = verticesNode->FirstChildElement("vertex"); for(vertexNode; vertexNode; vertexNode=vertexNode->NextSiblingElement("vertex")) { vertexNode->QueryFloatAttribute("x", &vertices->x); vertexNode->QueryFloatAttribute("y", &vertices->y); vertexNode->QueryFloatAttribute("z", &vertices->z); vertices++; } } // Normals TiXmlElement * normalsNode = SubMeshNode->FirstChildElement("Normals"); if(normalsNode) { unsigned int numNormals = 0; normalsNode->QueryUIntAttribute("num", &numNormals); MVector3 * normals = subMesh->allocNormals(numNormals); // normal TiXmlElement * normalNode = normalsNode->FirstChildElement("normal"); for(normalNode; normalNode; normalNode=normalNode->NextSiblingElement("normal")) { normalNode->QueryFloatAttribute("x", &normals->x); normalNode->QueryFloatAttribute("y", &normals->y); normalNode->QueryFloatAttribute("z", &normals->z); normals->normalize(); normals++; } } // Tangents TiXmlElement * tangentsNode = SubMeshNode->FirstChildElement("Tangents"); if(tangentsNode) { unsigned int numTangents = 0; tangentsNode->QueryUIntAttribute("num", &numTangents); MVector3 * tangents = subMesh->allocTangents(numTangents); // tangent TiXmlElement * tangentNode = tangentsNode->FirstChildElement("tangent"); for(tangentNode; tangentNode; tangentNode=tangentNode->NextSiblingElement("tangent")) { tangentNode->QueryFloatAttribute("x", &tangents->x); tangentNode->QueryFloatAttribute("y", &tangents->y); tangentNode->QueryFloatAttribute("z", &tangents->z); tangents->normalize(); tangents++; } } // TexCoords TiXmlElement * texCoordsNode = SubMeshNode->FirstChildElement("TexCoords"); if(texCoordsNode) { // num unsigned int numTexCoords = 0; texCoordsNode->QueryUIntAttribute("num", &numTexCoords); MVector2 * texCoords = subMesh->allocTexCoords(numTexCoords); // mapChannels unsigned int numVertices = subMesh->getVerticesSize(); const char * mapChannelsData = texCoordsNode->Attribute("mapChannels"); // read channels if(mapChannelsData) { char str[256]; strcpy(str, mapChannelsData); char * pch; unsigned int offset = 0; pch = strtok(str, " "); while(pch != NULL) { unsigned int channel = 0; sscanf(pch, "%d", &channel); subMesh->setMapChannelOffset(channel, offset); pch = strtok(NULL, " "); offset += numVertices; } } // create default channels else if((numVertices > 0) && (numTexCoords > numVertices)) { unsigned int numChannels = numTexCoords / numVertices; for(unsigned int c=0; c<numChannels; c++) subMesh->setMapChannelOffset(c, numVertices*c); } // texCoord TiXmlElement * texCoordNode = texCoordsNode->FirstChildElement("texCoord"); for(texCoordNode; texCoordNode; texCoordNode=texCoordNode->NextSiblingElement("texCoord")) { texCoordNode->QueryFloatAttribute("x", &texCoords->x); texCoordNode->QueryFloatAttribute("y", &texCoords->y); texCoords++; } } // Colors TiXmlElement * colorsNode = SubMeshNode->FirstChildElement("Colors"); if(colorsNode) { unsigned int numColors = 0; colorsNode->QueryUIntAttribute("num", &numColors); MColor * colors = subMesh->allocColors(numColors); // color TiXmlElement * colorNode = colorsNode->FirstChildElement("color"); for(colorNode; colorNode; colorNode=colorNode->NextSiblingElement("color")) { float x = 1, y = 1, z = 1, w = 1; colorNode->QueryFloatAttribute("x", &x); colorNode->QueryFloatAttribute("y", &y); colorNode->QueryFloatAttribute("z", &z); colorNode->QueryFloatAttribute("w", &w); colors->r = (unsigned char)x*255; colors->g = (unsigned char)y*255; colors->b = (unsigned char)z*255; colors->a = (unsigned char)w*255;; colors++; } } // Indices TiXmlElement * indicesNode = SubMeshNode->FirstChildElement("Indices"); if(indicesNode) { M_TYPES indicesType; unsigned int vSize = subMesh->getVerticesSize(); if(vSize < 65536){ indicesType = M_USHORT; } else{ indicesType = M_UINT; } unsigned int numIndices = 0; indicesNode->QueryUIntAttribute("num", &numIndices); subMesh->allocIndices(numIndices, indicesType); // indices TiXmlElement * indexNode = indicesNode->FirstChildElement("index"); switch(indicesType) { case M_USHORT: { unsigned short * indices = (unsigned short *)subMesh->getIndices(); for(indexNode; indexNode; indexNode=indexNode->NextSiblingElement("index")) { unsigned int id; indexNode->QueryUIntAttribute("value", &id); *indices = (unsigned short)id; indices++; } } break; case M_UINT: { unsigned int * indices = (unsigned int *)subMesh->getIndices(); for(indexNode; indexNode; indexNode=indexNode->NextSiblingElement("index")) { indexNode->QueryUIntAttribute("value", indices); indices++; } } break; } } // Skins TiXmlElement * skinsNode = SubMeshNode->FirstChildElement("Skins"); if(skinsNode) { MSkinData * skinData = subMesh->createSkinData(); unsigned int numSkins = 0; skinsNode->QueryUIntAttribute("num", &numSkins); MSkinPoint * skinPoints = skinData->allocPoints(numSkins); // skin TiXmlElement * skinNode = skinsNode->FirstChildElement("skin"); for(skinNode; skinNode; skinNode=skinNode->NextSiblingElement("skin")) { unsigned int vertexId = 0; unsigned int numBones = 0; skinNode->QueryUIntAttribute("vertex", &vertexId); skinNode->QueryUIntAttribute("numBones", &numBones); if(numBones > 0) { skinPoints->setVertexId(vertexId); skinPoints->allocateBonesLinks(numBones); unsigned short * bonesIds = skinPoints->getBonesIds(); float * bonesWeights = skinPoints->getBonesWeights(); TiXmlElement * boneNode = skinNode->FirstChildElement("bone"); for(boneNode; boneNode; boneNode=boneNode->NextSiblingElement("bone")) { unsigned int id; boneNode->QueryUIntAttribute("id", &id); boneNode->QueryFloatAttribute("weight", bonesWeights); *bonesIds = id; bonesIds++; bonesWeights++; } } skinPoints++; } } // Displays TiXmlElement * displaysNode = SubMeshNode->FirstChildElement("Displays"); if(displaysNode) { unsigned int numDisplays = 0; displaysNode->QueryUIntAttribute("num", &numDisplays); subMesh->allocDisplays(numDisplays); // display TiXmlElement * displayNode = displaysNode->FirstChildElement("display"); for(displayNode; displayNode; displayNode=displayNode->NextSiblingElement("display")) { unsigned int begin, size, material, cullFace = 0; displayNode->QueryUIntAttribute("begin", &begin); displayNode->QueryUIntAttribute("size", &size); displayNode->QueryUIntAttribute("material", &material); displayNode->QueryUIntAttribute("cullFace", &cullFace); // create display MDisplay * display = subMesh->addNewDisplay(M_PRIMITIVE_TRIANGLES, begin, size); // set material if(material < mesh->getMaterialsNumber()) display->setMaterial(mesh->getMaterial(material)); // set cull mode M_CULL_MODES cullMode = M_CULL_BACK; if(cullFace == 1) cullMode = M_CULL_FRONT; else if(cullFace == 2) cullMode = M_CULL_NONE; display->setCullMode(cullMode); } } // generate tangents if needed if(! subMesh->getTangents()) generateTangents(subMesh); subMeshs++; } MLOG_DEBUG("xmlMeshLoad success: "<<numSubMeshs<<" submeshs found"); return true; }