Files
fmviewer3/project/fm_viewer/fav/opengl/Geometry.cpp
2026-02-21 17:11:31 +09:00

547 lines
16 KiB
C++

/******************************************************************************
QtAV: Multimedia framework based on Qt and FFmpeg
Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
* 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 <QtDebug>
#include <QtCore/qmath.h>
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>()
<< 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<Attribute>& 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>()
<< 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>()
<< 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