/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2016) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "../Geometry.h" #include #include namespace FAV { Attribute::Attribute(DataType type, int tupleSize, int offset, bool normalize) : m_normalize(normalize) , m_type(type) , m_tupleSize(tupleSize) , m_offset(offset) {} Attribute::Attribute(const QByteArray& name, DataType type, int tupleSize, int offset, bool normalize) : m_normalize(normalize) , m_type(type) , m_tupleSize(tupleSize) , m_offset(offset) , m_name(name) {} #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const Attribute &a) { dbg.nospace() << "attribute: " << a.name(); dbg.nospace() << ", offset " << a.offset(); dbg.nospace() << ", tupleSize " << a.tupleSize(); dbg.nospace() << ", dataType " << a.type(); dbg.nospace() << ", normalize " << a.normalize(); return dbg.space(); } #endif Geometry::Geometry(int vertexCount, int indexCount, DataType indexType) : m_primitive(TriangleStrip) , m_itype(indexType) , m_vcount(vertexCount) , m_icount(indexCount) {} int Geometry::indexDataSize() const { switch (indexType()) { case TypeU16: return indexCount()*2; case TypeU32: return indexCount()*4; default: return indexCount(); } } void Geometry::setIndexValue(int index, int value) { switch (indexType()) { case TypeU8: { quint8* d = (quint8*)m_idata.constData(); *(d+index) = value; } break; case TypeU16: { quint16* d = (quint16*)m_idata.constData(); *(d+index) = value; } break; case TypeU32: { quint32* d = (quint32*)m_idata.constData(); *(d+index) = value; } break; default: break; } } void Geometry::setIndexValue(int index, int v1, int v2, int v3) { // TODO: *(d + 3*index), *(d + 3*index + 1), *(d + 3*index + 2) switch (indexType()) { case TypeU8: { quint8* d = (quint8*)m_idata.constData(); *(d+index++) = v1; *(d+index++) = v2; *(d+index++) = v2; } break; case TypeU16: { quint16* d = (quint16*)m_idata.constData(); *(d+index++) = v1; *(d+index++) = v2; *(d+index++) = v3; } break; case TypeU32: { quint32* d = (quint32*)m_idata.constData(); *(d+index++) = v1; *(d+index++) = v2; *(d+index++) = v3; } break; default: break; } } void Geometry::dumpVertexData() { printf("vertex %p: ", m_vdata.constData()); const int n = stride()/sizeof(float); for (int i = 0; i < m_vcount; ++i) { const float* f = (const float*)(m_vdata.constData()+i*stride()); for (int j = 0; j < n; ++j) { printf("%f, ", *(f+j)); } printf(";"); } printf("\n");fflush(0); } void Geometry::dumpIndexData() { switch (indexType()) { case TypeU8: { quint8* d = (quint8*)m_idata.constData(); for (int i = 0; i < m_icount; ++i) printf("%u, ", *(d+i)); } break; case TypeU16: { quint16* d = (quint16*)m_idata.constData(); for (int i = 0; i < m_icount; ++i) printf("%u, ", *(d+i)); } break; case TypeU32: { quint32* d = (quint32*)m_idata.constData(); for (int i = 0; i < m_icount; ++i) printf("%u, ", *(d+i)); } break; default: break; } printf("\n");fflush(0); } void Geometry::allocate(int nbVertex, int nbIndex) { m_icount = nbIndex; m_vcount = nbVertex; m_vdata.resize(nbVertex*stride()); memset(m_vdata.data(), 0, m_vdata.size()); if (nbIndex <= 0) { m_idata.clear(); // required? return; } switch (indexType()) { case TypeU8: m_idata.resize(nbIndex*sizeof(quint8)); break; case TypeU16: m_idata.resize(nbIndex*sizeof(quint16)); break; case TypeU32: m_idata.resize(nbIndex*sizeof(quint32)); break; default: break; } memset((void*)m_idata.constData(), 0, m_idata.size()); } bool Geometry::compare(const Geometry *other) const { // this == other: attributes and stride can be different if (!other) return false; if (stride() != other->stride()) return false; return attributes() == other->attributes(); } TexturedGeometry::TexturedGeometry() : Geometry() , nb_tex(0) , geo_rect(-1, 1, 2, -2) // (-1, -1, 2, 2) flip y { setVertexCount(4); a = QVector() << Attribute(TypeF32, 2, 0) << Attribute(TypeF32, 2, 2*sizeof(float)) ; setTextureCount(1); } void TexturedGeometry::setTextureCount(int value) { if (value == nb_tex) return; texRect.resize(value); nb_tex = value; } int TexturedGeometry::textureCount() const { return nb_tex; } void TexturedGeometry::setPoint(int index, const QPointF &p, const QPointF &tp, int texIndex) { setGeometryPoint(index, p); setTexturePoint(index, tp, texIndex); } void TexturedGeometry::setGeometryPoint(int index, const QPointF &p) { float *v = (float*)(m_vdata.constData() + index*stride()); *v = p.x(); *(v+1) = p.y(); } void TexturedGeometry::setTexturePoint(int index, const QPointF &tp, int texIndex) { float *v = (float*)(m_vdata.constData() + index*stride() + (texIndex+1)*2*sizeof(float)); *v = tp.x(); *(v+1) = tp.y(); } void TexturedGeometry::setRect(const QRectF &r, const QRectF &tr, int texIndex) { setPoint(0, r.topLeft(), tr.topLeft(), texIndex); setPoint(1, r.bottomLeft(), tr.bottomLeft(), texIndex); switch (primitive()) { case TriangleStrip: setPoint(2, r.topRight(), tr.topRight(), texIndex); setPoint(3, r.bottomRight(), tr.bottomRight(), texIndex); break; case TriangleFan: setPoint(3, r.topRight(), tr.topRight(), texIndex); setPoint(2, r.bottomRight(), tr.bottomRight(), texIndex); break; case Triangles: break; default: break; } } void TexturedGeometry::setGeometryRect(const QRectF &r) { geo_rect = r; } void TexturedGeometry::setTextureRect(const QRectF &tr, int texIndex) { if (texRect.size() <= texIndex) texRect.resize(texIndex+1); texRect[texIndex] = tr; //qInfo() << "tr" << tr; } const QVector& TexturedGeometry::attributes() const { return a; } void TexturedGeometry::create() { allocate(vertexCount()); if (a.size()-1 < textureCount()) { // the first is position for (int i = a.size()-1; i < textureCount(); ++i) a << Attribute(TypeF32, 2, int((i+1)* 2*sizeof(float))); } else { a.resize(textureCount() + 1); } setGeometryPoint(0, geo_rect.topLeft()); setGeometryPoint(1, geo_rect.bottomLeft()); switch (primitive()) { case TriangleStrip: setGeometryPoint(2, geo_rect.topRight()); setGeometryPoint(3, geo_rect.bottomRight()); break; case TriangleFan: setGeometryPoint(3, geo_rect.topRight()); setGeometryPoint(2, geo_rect.bottomRight()); break; case Triangles: break; default: break; } for (int i = 0; i < texRect.size(); ++i) { const QRectF tr = texRect[i]; setTexturePoint(0, tr.topLeft(), i); setTexturePoint(1, tr.bottomLeft(), i); switch (primitive()) { case TriangleStrip: setTexturePoint(2, tr.topRight(), i); setTexturePoint(3, tr.bottomRight(), i); break; case TriangleFan: setTexturePoint(3, tr.topRight(), i); setTexturePoint(2, tr.bottomRight(), i); break; case Triangles: break; default: break; } } } #if (TOP_DOWN_360) #define HEMISPHERE_ONLY 1 #define HEMISPHERE_TEST 1 #define DRAW_DEBUG_GRAPH 0 Sphere::Sphere() : TexturedGeometry() , r(1) { setPrimitive(Triangles); setResolution(64, 129); a = QVector() << Attribute(TypeF32, 3, 0) << Attribute(TypeF32, 2, 3*sizeof(float)); } #else // DURLA 360 Sphere::Sphere() : TexturedGeometry() , r(1) { setPrimitive(Triangles); setResolution(64, 64); //setResolution(128, 128); a = QVector() << Attribute(TypeF32, 3, 0) << Attribute(TypeF32, 2, 3*sizeof(float)); } #endif void Sphere::setResolution(int w, int h) { ru = w; rv = h; #if (HEMISPHERE_ONLY) setVertexCount((ru+1)*((rv/2)+1)); #else setVertexCount((ru+1)*(rv+1)); #endif } void Sphere::setRadius(float value) { r = value; } float Sphere::radius() const { return r; } #if (TOP_DOWN_360) void Sphere::create() { // RU = W, RV = H 가로세로 셀의 개수 * QUAD(2 triangle) allocate(vertexCount(), ru*rv*3*2); // quads * 2 triangles, if (a.size()-1 < nb_tex) { // the first is position for (int i = a.size()-1; i < nb_tex; ++i) a << Attribute(TypeF32, 2, 3*sizeof(float) + int(i* 2*sizeof(float))); } else { a.resize(nb_tex + 1); } // TODO: use geo_rect? float *vd = (float*)m_vdata.constData(); const float dTheta = M_PI*2.0/float(ru); // 경도 변환 각도 (360 / x) const float dPhi = M_PI/float(rv); // 위도 변환 각도 (180 / y) #if(HEMISPHERE_ONLY) // https://stackoverrun.com/ko/q/734419 #if (DRAW_DEBUG_GRAPH) QString graph = ""; #endif for (int lat = rv/2; lat < rv; ++lat) { const float phi = M_PI_2 - float(lat+1)*dPhi; // 90 - 위도 * y float cosPhi = qCos(phi); const float sinPhi = qSin(phi); // if(lat == rv-1) { // cosPhi = 0; // } //const float v = 1.0f - float(lat)*dv; // flip y? for (int lon = 0; lon <= ru; ++lon) { const float theta = float(lon)*dTheta; const float cosTheta = qCos(theta); const float sinTheta = qSin(theta); #if (DRAW_DEBUG_GRAPH) int x = r*cosPhi*cosTheta * 1000.0; // X int y = r*sinPhi * 1000.0; // Z int z = r*cosPhi*sinTheta * 1000.0; // Y graph += QString().sprintf("%02d,%02d,%03d,%03d,%03d\n",lat,lon,x,y,z); #endif // DRAW_DEBUG_GRAPH *vd++ = r*cosPhi*cosTheta*SPHERE_SCALE; // X = radius * *vd++ = r*sinPhi*SPHERE_SCALE; // Y(Z) 1~-1 *vd++ = r*cosPhi*sinTheta*SPHERE_SCALE; // Z // texture for (int i = 0; i < nb_tex; ++i) { // GRID *vd++ = texRect[i].x()+texRect[i].width()/qreal(ru) * qreal(lon); *vd++ = texRect[i].y()+texRect[i].height()/qreal(rv/2) * qreal(lat-rv/2); } } } #if (DRAW_DEBUG_GRAPH) QFile file("C:\\home\\temp\\sphere.csv"); if(file.open(QIODevice::WriteOnly)) { file.write("LAT,LON,X,Y,Z\n"); file.write(graph.toUtf8()); file.close(); } qInfo() << graph; #endif // create index data if (m_icount > 0) { int idx = 0; for (int lat = 0; lat < rv/2; ++lat) { for (int lon = 0; lon < ru; ++lon) { const int ring = lat*(ru+1) + lon; const int ringNext = ring + ru+1; setIndexValue(idx, ring, ringNext, ring+1); setIndexValue(idx+3, ringNext, ringNext+1, ring+1); idx += 6; } } } m_icount /= 2; #else // HEMISPHERE_ONLY --> FULL SPHERE for (int lat = 0; lat <= rv; ++lat) { const float phi = M_PI_2 - float(lat)*dPhi; const float cosPhi = qCos(phi); const float sinPhi = qSin(phi); //const float v = 1.0f - float(lat)*dv; // flip y? for (int lon = 0; lon <= ru; ++lon) { const float theta = float(lon)*dTheta; const float cosTheta = qCos(theta); const float sinTheta = qSin(theta); //const float u = float(lon) * du; *vd++ = r*cosPhi*cosTheta;//2.0*float(lon)/float(ru) -1.0;// *vd++ = r*sinPhi;//2.0*float(lat)/float(rv)-1.0;// *vd++ = r*cosPhi*sinTheta; // texture for (int i = 0; i < nb_tex; ++i) { *vd++ = texRect[i].x()+texRect[i].width()/float(ru) * float(lon); *vd++ = texRect[i].y()+texRect[i].height()/float(rv) * float(lat); } } } // create index data if (m_icount > 0) { int idx = 0; for (int lat = 0; lat < rv; ++lat) { for (int lon = 0; lon < ru; ++lon) { const int ring = lat*(ru+1) + lon; const int ringNext = ring + ru+1; setIndexValue(idx, ring, ringNext, ring+1); setIndexValue(idx+3, ringNext, ringNext+1, ring+1); idx += 6; } } } #endif } #else // DUAL 360 void Sphere::create() { allocate(vertexCount(), ru*rv*3*2); // quads * 2 triangles, if (a.size()-1 < nb_tex) { // the first is position for (int i = a.size()-1; i < nb_tex; ++i) a << Attribute(TypeF32, 2, 3*sizeof(float) + int(i* 2*sizeof(float))); } else { a.resize(nb_tex + 1); } // TODO: use geo_rect? float *vd = (float*)m_vdata.constData(); const float dTheta = M_PI*2.0/float(ru); const float dPhi = M_PI/float(rv); //const float du = 1.0f/float(ru); //const float dv = 1.0f/float(rv); for (int lat = 0; lat <= rv; ++lat) { const float phi = M_PI_2 - float(lat)*dPhi; const float cosPhi = qCos(phi); const float sinPhi = qSin(phi); //const float v = 1.0f - float(lat)*dv; // flip y? for (int lon = 0; lon <= ru; ++lon) { const float theta = float(lon)*dTheta; const float cosTheta = qCos(theta); const float sinTheta = qSin(theta); //const float u = float(lon) * du; *vd++ = r*cosPhi*cosTheta;//2.0*float(lon)/float(ru) -1.0;// *vd++ = r*sinPhi;//2.0*float(lat)/float(rv)-1.0;// *vd++ = r*cosPhi*sinTheta; for (int i = 0; i < nb_tex; ++i) { *vd++ = texRect[i].x()+texRect[i].width()/float(ru) * float(lon); *vd++ = texRect[i].y()+texRect[i].height()/float(rv) * float(lat); } } } // create index data if (m_icount > 0) { int idx = 0; for (int lat = 0; lat < rv; ++lat) { for (int lon = 0; lon < ru; ++lon) { const int ring = lat*(ru+1) + lon; const int ringNext = ring + ru+1; setIndexValue(idx, ring, ringNext, ring+1); setIndexValue(idx+3, ringNext, ringNext+1, ring+1); idx += 6; } } } } #endif // DURL 360 } //namespace FAV