/* QGL Primitives Ivan Pelipenko peri4ko@yandex.ru This program 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 3 of the License, or (at your option) any later version. This program 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 program. If not, see . */ #include "glprimitives.h" #include "glmesh.h" Mesh * Primitive::plane(float width, float length) { Mesh * ret = new Mesh(); QVector & v(ret->vertices ()); QVector & n(ret->normals ()); QVector & t(ret->texcoords()); QVector< Vector3i> & i(ret->indicesTriangles ()); float hw = width / 2.f, hl = length / 2.f; for (int j = 0; j < 4; ++j) n << QVector3D(0., 0., 1.); t << QVector2D(0., 0.) << QVector2D(0., 1.) << QVector2D(1., 1.) << QVector2D(1., 0.); v << QVector3D(-hw, -hl, 0.) << QVector3D(-hw, hl, 0.) << QVector3D(hw, hl, 0.) << QVector3D(hw, -hl, 0.); i << Vector3i(0, 2, 1) << Vector3i(0, 3, 2); return ret; } Mesh * Primitive::cube(float width, float length, float height) { Mesh * ret = new Mesh(); QVector3D scale(width, length, height); QVector & v(ret->vertices ()); QVector & n(ret->normals ()); QVector & t(ret->texcoords()); QVector< Vector3i> & i(ret->indicesTriangles ()); float hs = 0.5f; int si = 0; QMatrix4x4 mat; si = v.size(); for (int j = 0; j < 4; ++j) n << QVector3D(0., -1., 0.); t << QVector2D(0., 0.) << QVector2D(1., 0.) << QVector2D(1., 1.) << QVector2D(0., 1.); v << QVector3D(-hs, -hs, -hs) << QVector3D(hs, -hs, -hs) << QVector3D(hs, -hs, hs) << QVector3D(-hs, -hs, hs); i << Vector3i(si + 0, si + 1, si + 2) << Vector3i(si + 0, si + 2, si + 3); for (int r = 0; r < 3; ++r) { si = v.size(); mat.rotate(90., 0., 0., 1.); QVector3D cn = mat.map(n[0]); for (int j = 0; j < 4; ++j) { n << cn; v << mat.map(QVector4D(v[j])).toVector3D(); } t << QVector2D(0., 0.) << QVector2D(1., 0.) << QVector2D(1., 1.) << QVector2D(0., 1.); i << Vector3i(si + 0, si + 1, si + 2) << Vector3i(si + 0, si + 2, si + 3); } mat.setToIdentity(); mat.rotate(90., 1., 0.,0.); for (int r = 0; r < 2; ++r) { si = v.size(); mat.rotate(180., 1., 0.,0.); QVector3D cn = mat.map(n[0]); for (int j = 0; j < 4; ++j) { n << cn; v << mat.map(QVector4D(v[j])).toVector3D(); } t << QVector2D(0., 0.) << QVector2D(1., 0.) << QVector2D(1., 1.) << QVector2D(0., 1.); i << Vector3i(si + 0, si + 1, si + 2) << Vector3i(si + 0, si + 2, si + 3); } for (int i = 0; i < v.size(); ++i) v[i] *= scale; return ret; } Mesh * Primitive::ellipsoid(int segments_wl, int segments_h, float radius, float end_angle) { Mesh * ret = new Mesh(); QVector & v(ret->vertices ()); QVector & n(ret->normals ()); QVector & t(ret->texcoords()); QVector< Vector3i> & ind(ret->indicesTriangles()); int hseg = segments_h + 1, wlseg = segments_wl + 1; double crw, crl, a, ch, twl; double eang = deg2rad * end_angle; QVector3D cp; for (int i = 0; i <= hseg; i++) { ch = -cos((double)i / hseg * M_PI); cp.setZ(ch * radius); twl = sqrt(1. - ch * ch); crw = twl * radius; crl = twl * radius; int cvcnt = wlseg * 2; for (int j = 0; j < cvcnt; j++) { a = (double)j / (cvcnt - 1) * eang; cp.setX(crl * cos(a)); cp.setY(crw * sin(a)); v << cp; t << QVector2D((double)j / (cvcnt - 1), ch/2.f + 0.5f); n << cp.normalized(); int si = v.size() - 1; if (j > 0 && i > 0) { ind << Vector3i(si - cvcnt - 1, si, si - 1); ind << Vector3i(si - cvcnt, si, si - cvcnt - 1); } } } if (end_angle < 360.) { Mesh * cap = Primitive::disc(segments_h+1, radius, 180); cap->rotatePoints(90, 0, 1, 0); cap->rotatePoints(-90, 0, 0, 1); ret->append(cap); cap->flipNormals(); cap->rotatePoints(end_angle, 0, 0, 1); ret->append(cap); delete cap; } return ret; } Mesh * Primitive::disc(int segments, float radius, float end_angle) { Mesh * ret = new Mesh(); QVector & v(ret->vertices ()); QVector & n(ret->normals ()); QVector & t(ret->texcoords()); QVector< Vector3i> & ind(ret->indicesTriangles()); segments = qMax(segments + 1, 4); QVector3D cp; v << QVector3D(); n << QVector3D(0, 0, 1); t << QVector2D(0.5f, 0.5f); end_angle *= deg2rad; for (int i = 0; i < segments; i++) { double a = (double)i / (segments - 1) * end_angle; cp.setX(radius * cos(a)); cp.setY(radius * sin(a)); v << cp; n << QVector3D(0, 0, 1); t << QVector2D(cp.x() / radius + 1, cp.y() / radius + 1); int si = v.size() - 1; if (i > 0) ind << Vector3i(si - 1, si, 0); } return ret; } QVector3D coneNormal(double r, double height, double ang) { QVector3D norm; norm.setX(r * cos(ang)); norm.setY(r * sin(ang)); norm.setZ(0.); double rl = norm.length(); double ca = atan2(rl, height); norm *= cos(ca); norm.setZ(norm.length() * tan(ca)); return norm.normalized(); } Mesh * Primitive::cone(int segments, float radius, float height) { Mesh * ret = new Mesh(); QVector & v(ret->vertices ()); QVector & n(ret->normals ()); QVector & t(ret->texcoords()); QVector< Vector3i> & ind(ret->indicesTriangles()); int seg = qMax(segments + 1, 4); QVector3D cp; for (int i = 0; i < seg; i++) { double a = (double)i / (seg - 1) * M_2PI; cp.setX(radius * cos(a)); cp.setY(radius * sin(a)); if (i > 0) { v << QVector3D(0, 0, height); t << QVector2D((double)(i - 1) / (seg - 1), 1.f); double ta = ((double)i - 0.5) / (seg - 1) * M_2PI; n << coneNormal(radius, height, ta); } v << cp; t << QVector2D((double)i / (seg - 1), 0.f); n << coneNormal(radius, height, a); int si = v.size() - 1; if (i > 0) ind << Vector3i(si - 1, si - 2, si); } Mesh * cap = Primitive::disc(segments, radius); cap->flipNormals(); ret->append(cap); delete cap; return ret; } Mesh * Primitive::cylinder(int segments, float radius, float height, float end_angle) { Mesh * ret = new Mesh(); QVector & v(ret->vertices ()); QVector & n(ret->normals ()); QVector & t(ret->texcoords()); QVector< Vector3i> & ind(ret->indicesTriangles()); int seg = qMax(segments + 1, 4); QVector3D cp, norm; double eang = deg2rad * end_angle; for (int i = 0; i < seg; i++) { double a = (double)i / (seg - 1) * eang; cp.setX(radius * cos(a)); cp.setY(radius * sin(a)); cp.setZ(0.); norm = cp.normalized(); v << cp; cp.setZ(height); v << cp; t << QVector2D((double)i / (seg - 1), 0.f); t << QVector2D((double)i / (seg - 1), 1.f); n << norm; n << norm; int si = v.size() - 1; if (i > 0) { ind << Vector3i(si - 2, si - 1, si); ind << Vector3i(si - 1, si - 2, si - 3); } } Mesh * cap = Primitive::disc(segments, radius, end_angle); cap->flipNormals(); ret->append(cap); cap->translatePoints(QVector3D(0., 0., height)); cap->flipNormals(); ret->append(cap); delete cap; if (end_angle < 360.) { Mesh * cap = Primitive::plane(radius, height); cap->rotatePoints(90, 1, 0, 0); //cap->rotatePoints(-90, 0, 0, 1); cap->translatePoints(radius/2, 0, height/2); ret->append(cap); cap->flipNormals(); cap->rotatePoints(end_angle, 0, 0, 1); ret->append(cap); delete cap; } return ret; } Mesh * Primitive::arrow(int segments, float thick, float angle) { double cone_r = 1.5 * thick; double cone_h = 2. * cone_r / tan(angle * deg2rad); Mesh * ret = new Mesh(); Mesh * m = Primitive::cylinder(segments, thick / 2., 1. - cone_h); ret->append(m); delete m; m = Primitive::cone(segments, cone_r, cone_h); m->translatePoints(QVector3D(0., 0., 1. - cone_h)); ret->append(m); delete m; return ret; } Mesh * Primitive::torus(int segments_main, int segments_second, float radius_main, float radius_second, float end_angle) { Mesh * ret = new Mesh(); QVector & v(ret->vertices ()); QVector & n(ret->normals ()); QVector & t(ret->texcoords()); QVector< Vector3i> & ind(ret->indicesTriangles()); QVector cv, cn; QVector ct; segments_second = qMax(segments_second + 1, 4); for (int i = 0; i < segments_second; i++) { double x = (double)i / (segments_second - 1); double a = x * M_2PI; cv << QVector3D(radius_second * cos(a), 0., radius_second * sin(a)); cn << cv.back().normalized(); ct << QVector2D(0., x); cv.back() += QVector3D(radius_main, 0., 0.); } segments_main = qMax(segments_main + 1, 4); int ccnt = cv.size(), pcnt = 0; for (int i = 0; i < segments_main; i++) { double x = (double)i / (segments_main - 1); QMatrix4x4 rm; rm.rotate(x * end_angle, 0., 0., 1.); for (int j = 0; j < ccnt; j++) { ct[j].setX(x); v << rm.map(cv[j]); n << rm.map(cn[j]); } t.append(ct); if (i > 0) { for (int j = 0; j < ccnt - 1; j++) { ind << Vector3i(pcnt + j, pcnt + j + 1, pcnt + j - ccnt); ind << Vector3i(pcnt + j + 1, pcnt + j + 1 - ccnt, pcnt + j - ccnt); } } pcnt = v.size(); } if (end_angle < 360.) { Mesh * cap = Primitive::disc(segments_second-1, radius_second); cap->rotatePoints(90, 1, 0, 0); cap->translatePoints(radius_main, 0, 0); ret->append(cap); cap->flipNormals(); cap->rotatePoints(end_angle, 0, 0, 1); ret->append(cap); delete cap; } return ret; } Mesh * Primitive::cubeFrame(float width, float length, float height) { Mesh * ret = new Mesh(GL_LINES); QVector3D scale(width, length, height); QVector & v(ret->vertices ()); QVector & n(ret->normals ()); QVector & t(ret->texcoords()); QVector< Vector2i> & i(ret->indicesLines()); float hs = 0.5f; v << QVector3D(-hs, -hs, -hs) << QVector3D(-hs, hs, -hs) << QVector3D( hs, hs, -hs) << QVector3D( hs, -hs, -hs); v << QVector3D(-hs, -hs, hs) << QVector3D(-hs, hs, hs) << QVector3D( hs, hs, hs) << QVector3D( hs, -hs, hs); for (int j = 0; j < 8; ++j) { v[j] *= scale; t << QVector2D(0, 0); n << QVector3D(0,0,1); } for (int j = 0; j < 4; ++j) { i << Vector2i(j, (j + 1) % 4); i << Vector2i(j, j + 4); i << Vector2i(j + 4, (j + 1) % 4 + 4); } return ret; } Mesh * Primitive::ellipsoidFrame(int segments_wl, int segments_h, float radius) { Mesh * ret = new Mesh(GL_LINES); QVector & v(ret->vertices ()); QVector & n(ret->normals ()); QVector & t(ret->texcoords()); QVector< Vector2i> & ind(ret->indicesLines()); int hseg = segments_h + 1, wlseg = segments_wl + 1; double crw, crl, a, ch, twl; QVector3D cp; for (int i = 0; i <= hseg; i++) { ch = -cos((double)i / hseg * M_PI); cp.setZ(ch * radius); twl = sqrt(1. - ch * ch); crw = twl * radius; crl = twl * radius; int cvcnt = wlseg * 2; for (int j = 0; j < cvcnt; j++) { a = (double)j / (cvcnt - 1) * M_2PI; cp.setX(crl * cos(a)); cp.setY(crw * sin(a)); v << cp; t << QVector2D((double)j / (cvcnt - 1), ch/2.f + 0.5f); n << cp.normalized(); int si = v.size() - 1; if (j > 0 && i > 0) { ind << Vector2i(si, si - 1); ind << Vector2i(si - cvcnt, si); } } } return ret; } Mesh * Primitive::coneFrame(int segments, float radius, float height) { Mesh * ret = new Mesh(GL_LINES); QVector & v(ret->vertices ()); QVector & n(ret->normals ()); QVector & t(ret->texcoords()); QVector< Vector2i> & ind(ret->indicesLines()); int seg = qMax(segments + 1, 4); QVector3D cp; for (int i = 0; i < seg; i++) { double a = (double)i / (seg - 1) * M_2PI; cp.setX(radius * cos(a)); cp.setY(radius * sin(a)); if (i > 0) { v << QVector3D(0, 0, height); t << QVector2D((double)(i - 1) / (seg - 1), 1.f); double ta = ((double)i - 0.5) / (seg - 1) * M_2PI; n << coneNormal(radius, height, ta); } v << cp; t << QVector2D((double)i / (seg - 1), 0.f); n << coneNormal(radius, height, a); int si = v.size() - 1; if (i > 0) { ind << Vector2i(si - 1, si); ind << Vector2i(si - 2, si); } } return ret; } Mesh * Primitive::lineFrame(QVector3D p0, QVector3D p1) { Mesh * ret = new Mesh(GL_LINES); QVector & v(ret->vertices ()); QVector & n(ret->normals ()); QVector & t(ret->texcoords()); QVector< Vector2i> & ind(ret->indicesLines()); v << p0 << p1; n << QVector3D(0,0,1) << QVector3D(0,0,1); t << QVector2D(0,0) << QVector2D(1,0); ind << Vector2i(0, 1); return ret; }