void tst_QGeometryData::appendNormal() { QVector3D a(1.1, 1.2, 1.3); QVector3D b(2.1, 2.2, 2.3); QVector3D c(3.1, 3.2, 3.3); QVector3D d(4.1, 4.2, 4.3); { QGeometryData data; data.appendNormal(a); QCOMPARE(data.count(), 1); QCOMPARE(data.fields(), QGL::fieldMask(QGL::Normal)); QCOMPARE(data.normals().count(), 1); QCOMPARE(data.normals().at(0), a); } { QGeometryData data; data.appendNormal(a, b); QCOMPARE(data.count(), 2); QCOMPARE(data.fields(), QGL::fieldMask(QGL::Normal)); QCOMPARE(data.normals().count(), 2); QCOMPARE(data.normals().at(0), a); QCOMPARE(data.normals().at(1), b); } { QGeometryData data; data.appendNormal(a, b, c); QCOMPARE(data.count(), 3); QCOMPARE(data.fields(), QGL::fieldMask(QGL::Normal)); QCOMPARE(data.normals().count(), 3); QCOMPARE(data.normals().at(0), a); QCOMPARE(data.normals().at(1), b); QCOMPARE(data.normals().at(2), c); } { QGeometryData data; data.appendNormal(a, b, c, d); QCOMPARE(data.count(), 4); QCOMPARE(data.fields(), QGL::fieldMask(QGL::Normal)); QCOMPARE(data.normals().count(), 4); QCOMPARE(data.normals().at(0), a); QCOMPARE(data.normals().at(1), b); QCOMPARE(data.normals().at(2), c); QCOMPARE(data.normals().at(3), d); } { QGeometryData data; data.appendNormal(a, b, c, d); data.appendNormal(a, b, c, d); data.appendNormal(a); QCOMPARE(data.count(), 9); QCOMPARE(data.fields(), QGL::fieldMask(QGL::Normal)); QCOMPARE(data.normals().count(), 9); QCOMPARE(data.normals().at(0), a); QCOMPARE(data.normals().at(1), b); QCOMPARE(data.normals().at(5), b); QCOMPARE(data.normals().at(8), a); } }
void QGLBezierPatchesPrivate::subdivide(QGLBuilder *list) const { QGeometryData prim; int count = positions.size(); for (int posn = 0; (posn + 15) < count; posn += 16) { // Construct a QGLBezierPatch object from the next high-level patch. QGLBezierPatch patch; int vertex; for (int vertex = 0; vertex < 16; ++vertex) patch.points[vertex] = positions[posn + vertex]; QVector2D tex1, tex2; if (!textureCoords.isEmpty()) { tex1 = textureCoords[(posn / 16) * 2]; tex2 = textureCoords[(posn / 16) * 2 + 1]; } else { tex1 = QVector2D(0.0f, 0.0f); tex2 = QVector2D(1.0f, 1.0f); } qreal xtex = tex1.x(); qreal ytex = tex1.y(); qreal wtex = tex2.x() - xtex; qreal htex = tex2.y() - ytex; for (int corner = 0; corner < 4; ++corner) { vertex = posn + cornerOffsets[corner]; QVector3D n = patch.normal(cornerS[corner], cornerT[corner]); patch.indices[corner] = prim.count(); prim.appendVertex(patch.points[cornerOffsets[corner]]); prim.appendNormal(n); prim.appendTexCoord (QVector2D(xtex + wtex * cornerS[corner], ytex + htex * cornerT[corner])); } // Subdivide the patch and generate the final triangles. patch.recursiveSubDivide(&prim, subdivisionDepth, xtex, ytex, wtex, htex); } list->addTriangles(prim); }
void tst_QGeometryData::appendVertexNormal() { QVector3D a(1.1, 1.2, 1.3); QVector3D b(2.1, 2.2, 2.3); QVector3D c(3.1, 3.2, 3.3); QVector3D d(4.1, 4.2, 4.3); QVector3D an(5.1, 5.2, 5.3); QVector3D bn(6.1, 6.2, 6.3); QVector3D cn(7.1, 7.2, 7.3); QVector3D dn(8.1, 8.2, 8.3); { QGeometryData data; data.appendVertex(a); data.appendNormal(an); QCOMPARE(data.count(), 1); QCOMPARE(data.fields(), QGL::fieldMask(QGL::Position) | QGL::fieldMask(QGL::Normal)); QCOMPARE(data.vertices().count(), 1); QCOMPARE(data.vertices().at(0), a); } { QGeometryData data; data.appendVertex(a, b); data.appendNormal(an, bn); QCOMPARE(data.count(), 2); QCOMPARE(data.fields(), QGL::fieldMask(QGL::Position) | QGL::fieldMask(QGL::Normal)); QCOMPARE(data.vertices().count(), 2); QCOMPARE(data.vertices().at(0), a); QCOMPARE(data.vertex(1), b); QCOMPARE(data.normals().count(), 2); QCOMPARE(data.normal(0), an); QCOMPARE(data.normals().at(1), bn); } { QGeometryData data; data.appendVertex(a, b, c); data.appendNormal(an, bn, cn); QCOMPARE(data.count(), 3); QCOMPARE(data.fields(), QGL::fieldMask(QGL::Position) | QGL::fieldMask(QGL::Normal)); QCOMPARE(data.vertices().count(), 3); QCOMPARE(data.vertices().at(0), a); QCOMPARE(data.vertices().at(1), b); QCOMPARE(data.vertices().at(2), c); QCOMPARE(data.normals().count(), 3); QCOMPARE(data.normal(0), an); QCOMPARE(data.normals().at(1), bn); QCOMPARE(data.normal(2), cn); } { QGeometryData data; data.appendVertex(a, b, c, d); data.appendNormal(an, bn, cn, dn); QCOMPARE(data.count(), 4); QCOMPARE(data.fields(), QGL::fieldMask(QGL::Position) | QGL::fieldMask(QGL::Normal)); QCOMPARE(data.vertices().count(), 4); QCOMPARE(data.vertices().at(0), a); QCOMPARE(data.vertices().at(1), b); QCOMPARE(data.vertices().at(2), c); QCOMPARE(data.vertices().at(3), d); QCOMPARE(data.normals().count(), 4); QCOMPARE(data.normals().at(0), an); QCOMPARE(data.normals().at(1), bn); QCOMPARE(data.normals().at(2), cn); QCOMPARE(data.normals().at(3), dn); } { QGeometryData data; data.appendVertex(a, b, c, d); data.appendNormal(an, bn, cn, dn); data.appendVertex(a, b, c, d); data.appendNormal(an, bn, cn, dn); data.appendVertex(a); data.appendNormal(an); QCOMPARE(data.count(), 9); QCOMPARE(data.fields(), QGL::fieldMask(QGL::Position) | QGL::fieldMask(QGL::Normal)); QCOMPARE(data.vertices().count(), 9); QCOMPARE(data.vertices().at(0), a); QCOMPARE(data.vertices().at(1), b); QCOMPARE(data.vertices().at(5), b); QCOMPARE(data.vertices().at(8), a); QCOMPARE(data.normals().count(), 9); QCOMPARE(data.normals().at(0), an); QCOMPARE(data.normals().at(1), bn); QCOMPARE(data.normals().at(5), bn); QCOMPARE(data.normals().at(8), an); } }
PlyLoader::PlyLoader( const std::string& file, boost::optional< QColor4ub > color, double scale ) : color_( color ), scale_( scale ) { std::ifstream stream( file.c_str() ); std::string line; std::getline( stream, line ); if( line != "ply" ) { COMMA_THROW( comma::exception, "expected ply file; got \"" << line << "\" in " << file ); } unsigned int numVertex = 0; unsigned int numFace = 0; bool has_normals = false; std::vector< std::string > fields; while( stream.good() && !stream.eof() && line != "end_header" ) { std::getline( stream, line ); if( line.empty() ) { continue; } std::vector< std::string > v = comma::split( comma::strip( line ), ' ' ); if( v[0] == "element" ) // quick and dirty { if( v[1] == "vertex" ) { numVertex = boost::lexical_cast< unsigned int >( v[2] ); } else if( v[1] == "face" ) { numFace = boost::lexical_cast< unsigned int >( v[2] ); } } else if( v[0] == "format" && v[1] != "ascii" ) { COMMA_THROW( comma::exception, "only ascii supported; got: " << v[1] ); } else if( line == "property float x" ) { fields.push_back( "point/x" ); } else if( line == "property float y" ) { fields.push_back( "point/y" ); } else if( line == "property float z" ) { fields.push_back( "point/z" ); } else if( line == "property float nx" ) { fields.push_back( "normal/x" ); has_normals = true; } else if( line == "property float ny" ) { fields.push_back( "normal/y" ); } else if( line == "property float nz" ) { fields.push_back( "normal/z" ); } else if( line == "property uchar red" ) { fields.push_back( "r" ); } else if( line == "property uchar green" ) { fields.push_back( "g" ); } else if( line == "property uchar blue" ) { fields.push_back( "b" ); } else if( line == "property uchar alpha" ) { fields.push_back( "a" ); } } comma::csv::options csv; csv.fields = comma::join( fields, ',' ); csv.full_xpath = true; csv.delimiter = ' '; comma::csv::ascii< ply_vertex > ascii( csv ); QGeometryData geometry; QArray< QVector3D > vertices; QArray< QColor4ub > colors; for( unsigned int i = 0; i < numVertex; i++ ) { std::string s; if( stream.eof() ) { break; } std::getline( stream, s ); if( s.empty() ) { continue; } ply_vertex v; if( color_ ) { v.color = *color_; } // quick and dirty ascii.get( v, s ); if( numFace > 0 ) { geometry.appendVertex( QVector3D( v.point.x() * scale_, v.point.y() * scale_, v.point.z() * scale_ ) ); if( has_normals ) { geometry.appendNormal( QVector3D( v.normal.x(), v.normal.y(), v.normal.z() ) ); } geometry.appendColor( v.color ); } else { vertices.append( QVector3D( v.point.x() * scale_, v.point.y() * scale_, v.point.z() * scale_ ) ); // todo: normals? colors.append( v.color ); } } if( numFace > 0 ) { for( unsigned int i = 0; i < numFace; i++ ) // quick and dirty { std::string s; if( stream.eof() ) { break; } std::getline( stream, s ); if( s.empty() ) { continue; } std::vector< std::string > v = comma::split( comma::strip( s ), ' ' ); unsigned int vertices_per_face = boost::lexical_cast< unsigned int >( v[0] ); if( ( vertices_per_face + 1 ) != v.size() ) { COMMA_THROW( comma::exception, "invalid line \"" << s << "\"" ); } QGL::IndexArray indices; switch( vertices_per_face ) { case 3: for( unsigned int i = 0; i < 3; ++i ) { indices.append( boost::lexical_cast< unsigned int >( v[i+1] ) ); } break; case 4: // quick and dirty for now: triangulate boost::array< unsigned int, 4 > a; for( unsigned int i = 0; i < 4; ++i ) { a[i] = boost::lexical_cast< unsigned int >( v[i+1] ); } indices.append( a[0] ); indices.append( a[1] ); indices.append( a[2] ); indices.append( a[0] ); indices.append( a[2] ); indices.append( a[3] ); break; default: // never here break; } geometry.appendIndices( indices ); } QGLBuilder builder; builder.addTriangles( geometry ); //switch( vertices_per_face ) //{ // case 3: builder.addTriangles( geometry ); break; // case 4: builder.addQuads( geometry ); break; // default: COMMA_THROW( comma::exception, "only triangles and quads supported; but got " << vertices_per_face << " vertices per face" ); //} m_sceneNode = builder.finalizedSceneNode(); } else { m_vertices.addAttribute( QGL::Position, vertices ); // todo: normals? m_vertices.addAttribute( QGL::Color, colors ); m_vertices.upload(); m_sceneNode = NULL; } stream.close(); }
QGeometryData MgGeometriesData::sphere(qreal radius,int divisions) { QGeometryData geometry; // Determine the number of slices and stacks to generate. static int const slicesAndStacks[] = { 4, 4, 8, 4, 8, 8, 16, 8, 16, 16, 32, 16, 32, 32, 64, 32, 64, 64, 128, 64, 128, 128 }; if (divisions < 1) divisions = 1; else if (divisions > 10) divisions = 10; int stacks = slicesAndStacks[divisions * 2 - 1]; int slices = slicesAndStacks[divisions * 2 - 2]; // Precompute sin/cos values for the slices and stacks. const int maxSlices = 128 + 1; const int maxStacks = 128 + 1; qreal sliceSin[maxSlices]; qreal sliceCos[maxSlices]; qreal stackSin[maxStacks]; qreal stackCos[maxStacks]; for (int slice = 0; slice < slices; ++slice) { qreal angle = 2 * M_PI * slice / slices; sliceSin[slice] = qFastSin(angle); sliceCos[slice] = qFastCos(angle); } sliceSin[slices] = sliceSin[0]; // Join first and last slice. sliceCos[slices] = sliceCos[0]; for (int stack = 0; stack <= stacks; ++stack) { qreal angle = M_PI * stack / stacks; stackSin[stack] = qFastSin(angle); stackCos[stack] = qFastCos(angle); } stackSin[0] = 0.0f; // Come to a point at the poles. stackSin[stacks] = 0.0f; // Create the stacks. for (int stack = 0; stack < stacks; ++stack) { QGeometryData prim; qreal z = radius * stackCos[stack]; qreal nextz = radius * stackCos[stack + 1]; qreal s = stackSin[stack]; qreal nexts = stackSin[stack + 1]; qreal c = stackCos[stack]; qreal nextc = stackCos[stack + 1]; qreal r = radius * s; qreal nextr = radius * nexts; for (int slice = 0; slice <= slices; ++slice) { prim.appendVertex (QVector3D(nextr * sliceSin[slice], nextr * sliceCos[slice], nextz)); prim.appendNormal (QVector3D(sliceSin[slice] * nexts, sliceCos[slice] * nexts, nextc)); prim.appendVertex (QVector3D(r * sliceSin[slice], r * sliceCos[slice], z)); prim.appendNormal (QVector3D(sliceSin[slice] * s, sliceCos[slice] * s, c)); } geometry.appendGeometry(prim); } return geometry; }
/*! \relates QGLSphere Builds the geometry for \a sphere within the specified display \a list. */ QGLBuilder& operator<<(QGLBuilder& list, const QGLSphere& sphere) { qreal scale = sphere.diameter(); int divisions = qMax(sphere.subdivisionDepth() - 1, 0); // define a 0 division sphere as 4 points around the equator, 4 points around the bisection at poles. // each division doubles the number of points. // since each pass of each loop does half a sphere, we multiply by 2 rather than 4. int total = 2*(1 << divisions); //list.begin(QGL::TRIANGLE); //QGeometryData *prim = list.currentPrimitive(); QGeometryData prim; const QVector3D initialVector(0, 0, 1); const QVector3D zAxis(0, 0, 1); const QVector3D yAxis(0, 1, 0); for(int vindex = 0; vindex < total; vindex++) { qreal vFrom = qreal(vindex) / qreal(total); qreal vTo = qreal(vindex+1) / qreal(total); QQuaternion ryFrom = QQuaternion::fromAxisAndAngle(yAxis, 180.0f * vFrom); QQuaternion ryTo = QQuaternion::fromAxisAndAngle(yAxis, 180.0f * vTo); for (int uindex = 0; uindex < 2*total; uindex++) { qreal uFrom = qreal(uindex) / qreal(total); qreal uTo = qreal(uindex+1) / qreal(total); QQuaternion rzFrom = QQuaternion::fromAxisAndAngle(zAxis, 180.0f * uFrom); QQuaternion rzTo = QQuaternion::fromAxisAndAngle(zAxis, 180.0f * uTo); // four points QVector3D na, nb, nc, nd; QVector3D va, vb, vc, vd; na = ryFrom.rotatedVector(initialVector); na = rzFrom.rotatedVector(na); nb = ryTo.rotatedVector(initialVector); nb = rzFrom.rotatedVector(nb); nc = ryTo.rotatedVector(initialVector); nc = rzTo.rotatedVector(nc); nd = ryFrom.rotatedVector(initialVector); nd = rzTo.rotatedVector(nd); QVector2D ta(uFrom/2.0f, 1.0-vFrom); QVector2D tb(uFrom/2.0f, 1.0-vTo); QVector2D tc(uTo/2.0f, 1.0-vTo); QVector2D td(uTo/2.0f, 1.0-vFrom); va = na * scale / 2.0f; vb = nb * scale / 2.0f; vc = nc * scale / 2.0f; vd = nd * scale / 2.0f; prim.appendVertex(va, vb, vc); prim.appendNormal(na, nb, nc); prim.appendTexCoord(ta, tb, tc); prim.appendVertex(va, vc, vd); prim.appendNormal(na, nc, nd); prim.appendTexCoord(ta, tc, td); } } list.addTriangles(prim); return list; }
//------------------------------------------------------------------------------ QGLBuilder& operator << ( QGLBuilder& builder, const QGLEllipsoid& ellipsoid ) { // Determine the number of slices and stacks to generate. static int const numberOfSlicesForSubdivisionDepth[] = { 8, 8, 16, 16, 32, 32, 64, 64, 128, 128 }; static int const numberOfStacksForSubdivisionDepth[] = { 4, 8, 8, 16, 16, 32, 32, 64, 64, 128 }; const unsigned int numberOfSlices = numberOfSlicesForSubdivisionDepth[ ellipsoid.GetSubdivisionDepth() - 1 ]; const unsigned int numberOfStacks = numberOfStacksForSubdivisionDepth[ ellipsoid.GetSubdivisionDepth() - 1 ]; // Precompute sin/cos values for the slices. const unsigned int maxSlices = 128 + 1; const unsigned int maxStacks = 128 + 1; qreal sliceSin[ maxSlices ]; qreal sliceCos[ maxSlices ]; for( unsigned int slice = 0; slice < numberOfSlices; ++slice ) { const qreal angle = 2 * M_PI * (numberOfSlices - 1 - slice) / numberOfSlices; sliceSin[slice] = qFastSin(angle); sliceCos[slice] = qFastCos(angle); } // Join first and last slice. sliceSin[numberOfSlices] = sliceSin[0]; sliceCos[numberOfSlices] = sliceCos[0]; // Precompute sin/cos values for the stacks. qreal stackSin[ maxStacks ]; qreal stackCos[ maxStacks ]; for( unsigned int stack = 0; stack <= numberOfStacks; ++stack ) { // Efficiently handle end-points which also ensure geometry comes to a point at the poles (no round-off). if( stack == 0 ) { stackSin[stack] = 0.0f; stackCos[stack] = 1.0f; } else if( stack == numberOfStacks ) { stackSin[stack] = 0.0f; stackCos[stack] = -1.0f; } else { const qreal angle = M_PI * stack / numberOfStacks; stackSin[stack] = qFastSin(angle); stackCos[stack] = qFastCos(angle); } } // Half the dimensions of the ellipsoid for calculations below (centroid of ellipsoid is 0, 0, 0.) const qreal xRadius = 0.5 * ellipsoid.GetXDiameter(); const qreal yRadius = 0.5 * ellipsoid.GetYDiameter(); const qreal zRadius = 0.5 * ellipsoid.GetZDiameter(); const qreal oneOverXRadiusSquared = 1.0 / (xRadius * xRadius); const qreal oneOverYRadiusSquared = 1.0 / (yRadius * yRadius); const qreal oneOverZRadiusSquared = 1.0 / (zRadius * zRadius); // Create the stacks. for( unsigned int stack = 0; stack < numberOfStacks; ++stack ) { QGeometryData quadStrip; for( unsigned int slice = 0; slice <= numberOfSlices; ++slice ) { // Equation for ellipsoid surface is x^2/xRadius^2 + y^2/yRadius^2 + z^2/zRadius^2 = 1 // Location of vertices can be specified in terms of "polar coordinates". const qreal nextx = xRadius * stackSin[stack+1] * sliceSin[slice]; const qreal nexty = yRadius * stackSin[stack+1] * sliceCos[slice]; const qreal nextz = zRadius * stackCos[stack+1]; quadStrip.appendVertex( QVector3D( nextx, nexty, nextz) ); // Equation for ellipsoid surface is Surface = x^2/xRadius^2 + y^2/yRadius^2 + z^2/zRadius^2 - 1 // Gradient for ellipsoid is x/xRadius^2*Nx> + y/yRadius^2*Ny> + z/zRadius^2*Nz> // Gradient for sphere simplifies to x*Nx> + y*Ny> + z*Nz> // const qreal nextGradientx = stackSin[stack+1] * sliceSin[slice]; // const qreal nextGradienty = stackSin[stack+1] * sliceCos[slice]; // const qreal nextGradientz = stackCos[stack+1]; const qreal nextGradientx = nextx * oneOverXRadiusSquared; const qreal nextGradienty = nexty * oneOverYRadiusSquared; const qreal nextGradientz = nextz * oneOverZRadiusSquared; const qreal nextGradientMagSquared = nextGradientx * nextGradientx + nextGradienty * nextGradienty + nextGradientz * nextGradientz; const qreal oneOverNextGradientMagnitude = 1.0 / sqrt( nextGradientMagSquared ); quadStrip.appendNormal( oneOverNextGradientMagnitude * QVector3D( nextGradientx, nextGradienty, nextGradientz ) ); quadStrip.appendTexCoord( QVector2D(1.0f - qreal(slice) / numberOfSlices, 1.0f - qreal(stack + 1) / numberOfStacks) ); const qreal x = xRadius * stackSin[stack] * sliceSin[slice]; const qreal y = yRadius * stackSin[stack] * sliceCos[slice]; const qreal z = zRadius * stackCos[stack]; quadStrip.appendVertex( QVector3D( x, y, z) ); // const qreal gradientx = stackSin[stack] * sliceSin[slice]; // const qreal gradienty = stackSin[stack] * sliceCos[slice]; // const qreal gradientz = stackCos[stack]; const qreal gradientx = x * oneOverXRadiusSquared; const qreal gradienty = y * oneOverYRadiusSquared; const qreal gradientz = z * oneOverZRadiusSquared; const qreal gradientMagSquared = gradientx * gradientx + gradienty * gradienty + gradientz * gradientz; const qreal oneOverGradientMagnitude = 1.0 / sqrt( gradientMagSquared ); quadStrip.appendNormal( oneOverGradientMagnitude * QVector3D( gradientx, gradienty, gradientz) ); quadStrip.appendTexCoord( QVector2D(1.0f - qreal(slice) / numberOfSlices, 1.0f - qreal(stack) / numberOfStacks) ); } // The quad strip stretches from pole to pole. builder.addQuadStrip( quadStrip ); } return builder; }