b’triangle_renderer.tar.gz’
/***** BallMath.h – Essential routines for Arcball. *****/
#ifndef _H_BallMath
#define _H_BallMath
#include “BallAux.h”
HVect MouseOnSphere(HVect mouse, HVect ballCenter, double ballRadius);
HVect ConstrainToAxis(HVect loose, HVect axis);
int NearestConstraintAxis(HVect loose, HVect *axes, int nAxes);
Quat Qt_FromBallPoints(HVect from, HVect to);
void Qt_ToBallPoints(Quat q, HVect *arcFrom, HVect *arcTo);
#endif
triangle_renderer/BallAux.cpp
triangle_renderer/BallAux.cpp/***** BallAux.c *****/
#include
#include “BallAux.h”
Quat qOne = {0, 0, 0, 1};
/* Return quaternion product qL * qR. Note: order is important!
* To combine rotations, use the product Mul(qSecond, qFirst),
* which gives the effect of rotating by qFirst then qSecond. */
Quat Qt_Mul(Quat qL, Quat qR)
{
Quat qq;
qq.w = qL.w*qR.w – qL.x*qR.x – qL.y*qR.y – qL.z*qR.z;
qq.x = qL.w*qR.x + qL.x*qR.w + qL.y*qR.z – qL.z*qR.y;
qq.y = qL.w*qR.y + qL.y*qR.w + qL.z*qR.x – qL.x*qR.z;
qq.z = qL.w*qR.z + qL.z*qR.w + qL.x*qR.y – qL.y*qR.x;
return (qq);
}
/* Construct rotation matrix from (possibly non-unit) quaternion.
* Assumes matrix is used to multiply column vector on the left:
* vnew = mat vold. Works correctly for right-handed coordinate system
* and right-handed rotations. */
void Qt_ToMatrix(Quat q, HMatrix &out)
{
double Nq = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w;
double s = (Nq > 0.0) ? (2.0 / Nq) : 0.0;
double xs = q.x*s, ys = q.y*s, zs = q.z*s;
double wx = q.w*xs, wy = q.w*ys, wz = q.w*zs;
double xx = q.x*xs, xy = q.x*ys, xz = q.x*zs;
double yy = q.y*ys, yz = q.y*zs, zz = q.z*zs;
out[X][X] = 1.0 – (yy + zz); out[Y][X] = xy + wz; out[Z][X] = xz – wy;
out[X][Y] = xy – wz; out[Y][Y] = 1.0 – (xx + zz); out[Z][Y] = yz + wx;
out[X][Z] = xz + wy; out[Y][Z] = yz – wx; out[Z][Z] = 1.0 – (xx + yy);
out[X][W] = out[Y][W] = out[Z][W] = out[W][X] = out[W][Y] = out[W][Z] = 0.0;
out[W][W] = 1.0;
}
/* Return conjugate of quaternion. */
Quat Qt_Conj(Quat q)
{
Quat qq;
qq.x = -q.x; qq.y = -q.y; qq.z = -q.z; qq.w = q.w;
return (qq);
}
/* Return vector formed from components */
HVect V3_(float x, float y, float z)
{
HVect v;
v.x = x; v.y = y; v.z = z; v.w = 0;
return (v);
}
/* Return norm of v, defined as sum of squares of components */
float V3_Norm(HVect v)
{
return ( v.x*v.x + v.y*v.y + v.z*v.z );
}
/* Return unit magnitude vector in direction of v */
HVect V3_Unit(HVect v)
{
static HVect u = {0, 0, 0, 0};
float vlen = sqrt(V3_Norm(v));
if (vlen != 0.0) {
u.x = v.x/vlen; u.y = v.y/vlen; u.z = v.z/vlen;
}
return (u);
}
/* Return version of v scaled by s */
HVect V3_Scale(HVect v, float s)
{
HVect u;
u.x = s*v.x; u.y = s*v.y; u.z = s*v.z; u.w = v.w;
return (u);
}
/* Return negative of v */
HVect V3_Negate(HVect v)
{
static HVect u = {0, 0, 0, 0};
u.x = -v.x; u.y = -v.y; u.z = -v.z;
return (u);
}
/* Return sum of v1 and v2 */
HVect V3_Add(HVect v1, HVect v2)
{
static HVect v = {0, 0, 0, 0};
v.x = v1.x+v2.x; v.y = v1.y+v2.y; v.z = v1.z+v2.z;
return (v);
}
/* Return difference of v1 minus v2 */
HVect V3_Sub(HVect v1, HVect v2)
{
static HVect v = {0, 0, 0, 0};
v.x = v1.x-v2.x; v.y = v1.y-v2.y; v.z = v1.z-v2.z;
return (v);
}
/* Halve arc between unit vectors v0 and v1. */
HVect V3_Bisect(HVect v0, HVect v1)
{
HVect v = {0, 0, 0, 0};
float Nv;
v = V3_Add(v0, v1);
Nv = V3_Norm(v);
if (Nv < 1.0e-5) {
v = V3_(0, 0, 1);
} else {
v = V3_Scale(v, 1/sqrt(Nv));
}
return (v);
}
/* Return dot product of v1 and v2 */
float V3_Dot(HVect v1, HVect v2)
{
return (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z);
}
/* Return cross product, v1 x v2 */
HVect V3_Cross(HVect v1, HVect v2)
{
static HVect v = {0, 0, 0, 0};
v.x = v1.y*v2.z-v1.z*v2.y;
v.y = v1.z*v2.x-v1.x*v2.z;
v.z = v1.x*v2.y-v1.y*v2.x;
return (v);
}
/***** Ball.h *****/
#ifndef _H_Ball
#define _H_Ball
#include "BallAux.h"
#ifdef __APPLE__
#include
#else
#include
#endif
typedef enum AxisSet{NoAxes, CameraAxes, BodyAxes, OtherAxes, NSets} AxisSet;
typedef float *ConstraintSet;
typedef struct {
HVect center;
double radius;
Quat qNow, qDown, qDrag;
HVect vNow, vDown, vFrom, vTo, vrFrom, vrTo;
HMatrix mNow, mDown;
Bool showResult, dragging;
ConstraintSet sets[NSets];
int setSizes[NSets];
AxisSet axisSet;
int axisIndex;
} BallData;
/* Public routines */
void Ball_Init(BallData *ball);
void Ball_Place(BallData *ball, HVect center, double radius);
void Ball_Mouse(BallData *ball, HVect vNow);
void Ball_UseSet(BallData *ball, AxisSet axisSet);
void Ball_ShowResult(BallData *ball);
void Ball_HideResult(BallData *ball);
void Ball_Update(BallData *ball);
void Ball_Value(BallData *ball, GLfloat *mNow);
void Ball_BeginDrag(BallData *ball);
void Ball_EndDrag(BallData *ball);
void Ball_Draw(BallData *ball);
/* Private routines */
void DrawAnyArc(HVect vFrom, HVect vTo);
void DrawHalfArc(HVect n);
void Ball_DrawConstraints(BallData *ball);
void Ball_DrawDragArc(BallData *ball);
void Ball_DrawResultArc(BallData *ball);
#endif
///////////////////////////////////////////////////
//
// Hamish Carr
// January, 2018
//
// ————————
// GeometricSurfaceFaceDS.h
// ————————
//
// Base code for geometric assignments.
//
// This is the minimalistic Face-based D/S for storing
// surfaces, to be used as the basis for fuller versions
//
// It will include object load / save code & render code
//
///////////////////////////////////////////////////
#ifndef _GEOMETRIC_SURFACE_FACE_DS_H
#define _GEOMETRIC_SURFACE_FACE_DS_H
#include
#include “Cartesian3.h”
class GeometricSurfaceFaceDS
{ // class GeometricSurfaceFaceDS
public:
// vectors to store vertex and triangle information – relying on POD rule
std::vector
// bounding sphere size
float boundingSphereSize;
// midpoint of object
Cartesian3 midPoint;
// constructor will initialise to safe values
GeometricSurfaceFaceDS();
// read routine returns true on success, failure otherwise
bool ReadFileTriangleSoup(char *fileName);
// routine to render
void Render();
}; // class GeometricSurfaceFaceDS
#endif
///////////////////////////////////////////////////
//
// Hamish Carr
// January, 2018
//
// ————————
// Cartesian3.h
// ————————
//
// A minimal class for a point in Cartesian space
//
///////////////////////////////////////////////////
#ifndef CARTESIAN3_H
#define CARTESIAN3_H
#include
// the class – we will rely on POD for sending to GPU
class Cartesian3
{ // Cartesian3
public:
// the coordinates
float x, y, z;
// constructors
Cartesian3();
Cartesian3(float X, float Y, float Z);
Cartesian3(const Cartesian3 &other);
// equality operator
bool operator ==(const Cartesian3 &other);
// addition operator
Cartesian3 operator +(const Cartesian3 &other);
// subtraction operator
Cartesian3 operator -(const Cartesian3 &other);
// multiplication operator
Cartesian3 operator *(float factor);
// division operator
Cartesian3 operator /(float factor);
// crossproduct routine
Cartesian3 cross(const Cartesian3 &other);
// routine to find the length
float length();
// normalisation routine
Cartesian3 normalise();
}; // Cartesian3
// stream output
std::ostream & operator << (std::ostream &outStream, Cartesian3 value);
#endif
/***** BallAux.h - Vector and quaternion routines for Arcball. *****/
#ifndef _H_BallAux
#define _H_BallAux
typedef int Bool;
typedef struct {float x, y, z, w;} Quat;
enum QuatPart {X, Y, Z, W, QuatLen};
typedef Quat HVect;
typedef float HMatrix[QuatLen][QuatLen];
extern Quat qOne;
void Qt_ToMatrix(Quat q, HMatrix &out);
Quat Qt_Conj(Quat q);
Quat Qt_Mul(Quat qL, Quat qR);
HVect V3_(float x, float y, float z);
float V3_Norm(HVect v);
HVect V3_Add(HVect v1, HVect v2);
HVect V3_Unit(HVect v);
HVect V3_Scale(HVect v, float s);
HVect V3_Negate(HVect v);
HVect V3_Sub(HVect v1, HVect v2);
float V3_Dot(HVect v1, HVect v2);
HVect V3_Cross(HVect v1, HVect v2);
HVect V3_Bisect(HVect v0, HVect v1);
#endif
# University of Leeds
# COMP 5821M Triangle Soup Renderer
This is a very simple renderer for triangle soup. It makes no attempt to be efficient,
which keeps the code clearer.
============================
LINUX (FENG-LINUX) COMPILE:
============================
To compile on the University Linux machines, you will need to do the following:
[userid@machine triangle_renderer]$ module add qt/5.3.1
[userid@machine triangle_renderer]$ qmake -project QT+=opengl LIBS+=-lGLU
[userid@machine triangle_renderer]$ qmake
[userid@machine triangle_renderer]$ make
You should see a compiler warning about an unused parameter, which can be ignored.
To execute the renderer, pass the file name on the command line:
[userid@machine triangle_renderer]$ ./Assignment2MeshDSHandout ../models/tetrahedron.tri
On other Linux installs (such as your own machine), the details may vary.
============================
MAC OS 11 (BIG SUR) COMPILE:
============================
First you will need to install Qt. The simplest way to do this is to use the Homebrew
package manager:
https://brew.sh
Once you have installed this (following the instructions), you will want to use:
brew install qt@5
Note that the current version of Qt is 6, which the code will refuse to compile against,
so you must explicitly choose qt 5. If you have already installed Qt, you may need to
use /usr/local/Cellar/qt@5/5.15.2/bin/qmake instead of qmake
userid@machine triangle_renderer % qmake -project QT+=opengl CONFIG+=c++11
userid@machine triangle_renderer % qmake
userid@machine triangle_renderer % make
On MacOS, the executable is bundled as an application, so you will need to invoke it as
follows:
userid@machine triangle_renderer % ./triangle_renderer.app/Contents/MacOS/triangle_renderer ../models/tetrahedron.tri
============================
WINDOWS 10 COMPILE:
============================
Install Qt 5.13.0, it will come with QT creator.
It can be found here:
https://download.qt.io/archive/qt/5.13/5.13.0/qt-opensource-windows-x86-5.13.0.exe.mirrorlist
Once QT is installed, restart your computer.
Open Powershell on the folder where you have the source code
- can be done by shift+right click in the explorer window, and choosing open powershell
- or open powershell, cd "directory"
Run qmake -project QT+=opengl LIBS+=-lopengl32
Double click the .pro file that was generated: this will open QTCreator.
Select a compiler version that fits your machine (e.g. MSVC2017 64bit).
Click details to see where the different configurations (debug, release) will be built, and change it if you like.
Click Configure project.
Click Build to confirm the compilation is correct, and produces no errors.
The executable can be ran from the powershell going to the build folder you defined by doing
./triangle_renderer.exe ../models/tetrahedron.tri
Alternatively, you can click "Projects" on QTCreator, click "run" under your configuration, and write
../models/tetrahedron.tri
where it says "Command line arguments". This will allow running with a parameter when you click the green arrow.
triangle_renderer/Cartesian3.cpp
triangle_renderer/Cartesian3.cpp///////////////////////////////////////////////////
//
// Hamish Carr
// January, 2018
//
// ------------------------
// Cartesian3.h
// ------------------------
//
// A minimal class for a point in Cartesian space
//
///////////////////////////////////////////////////
#include "Cartesian3.h"
#include "math.h"
// constructors
Cartesian3::Cartesian3()
: x(0.0), y(0.0), z(0.0)
{}
Cartesian3::Cartesian3(float X, float Y, float Z)
: x(X), y(Y), z(Z)
{}
Cartesian3::Cartesian3(const Cartesian3 &other)
: x(other.x), y(other.y), z(other.z)
{}
// equality operator
bool Cartesian3::operator ==(const Cartesian3 &other)
{ // Cartesian3::operator ==()
return ((x == other.x) && (y == other.y) && (z == other.z));
} // Cartesian3::operator ==()
// addition operator
Cartesian3 Cartesian3::operator +(const Cartesian3 &other)
{ // Cartesian3::operator +()
Cartesian3 returnVal(x + other.x, y + other.y, z + other.z);
return returnVal;
} // Cartesian3::operator +()
// subtraction operator
Cartesian3 Cartesian3::operator -(const Cartesian3 &other)
{ // Cartesian3::operator -()
Cartesian3 returnVal(x - other.x, y - other.y, z - other.z);
return returnVal;
} // Cartesian3::operator -()
// multiplication operator
Cartesian3 Cartesian3::operator *(float factor)
{ // Cartesian3::operator *()
Cartesian3 returnVal(x * factor, y * factor, z * factor);
return returnVal;
} // Cartesian3::operator *()
// division operator
Cartesian3 Cartesian3::operator /(float factor)
{ // Cartesian3::operator /()
Cartesian3 returnVal(x / factor, y / factor, z / factor);
return returnVal;
} // Cartesian3::operator /()
// crossproduct routine
Cartesian3 Cartesian3::cross(const Cartesian3 &other)
{ // Cartesian3::operator ==()
Cartesian3 returnVal(y * other.z - z * other.y, z * other.x - x * other.z, x * other.y - y * other.x);
return returnVal;
} // Cartesian3::operator ==()
// routine to find the length
float Cartesian3::length()
{ // Cartesian3::length()
return (x*x + y*y + z*z);
} // Cartesian3::length()
// normalisation routine
Cartesian3 Cartesian3::normalise()
{ // Cartesian3::normalise()
float length = sqrt(x*x+y*y+z*z);
Cartesian3 returnVal(x/length, y/length, z/length);
return returnVal;
} // Cartesian3::normalise()
// stream output
std::ostream & operator << (std::ostream &outStream, Cartesian3 value)
{ // stream output
outStream << value.x << " " << value.y << " " << value.z;
return outStream;
} // stream output
triangle_renderer/GeometricSurfaceFaceDS.cpp
triangle_renderer/GeometricSurfaceFaceDS.cpp///////////////////////////////////////////////////
//
// Hamish Carr
// January, 2018
//
// ------------------------
// GeometricSurfaceFaceDS.cpp
// ------------------------
//
// Base code for geometric assignments.
//
// This is the minimalistic Face-based D/S for storing
// surfaces, to be used as the basis for fuller versions
//
// It will include object load / save code & render code
//
///////////////////////////////////////////////////
#include "GeometricSurfaceFaceDS.h"
#include
#include
#include
#ifdef __APPLE__
#include
#include
#else
#include
#include
#endif
// constructor will initialise to safe values
GeometricSurfaceFaceDS::GeometricSurfaceFaceDS()
{ // GeometricSurfaceFaceDS::GeometricSurfaceFaceDS()
// force the size to nil (should not be necessary, but . . .)
vertices.resize(0);
// set this to something reasonable
boundingSphereSize = 1.0;
// set the midpoint to the origin
midPoint = Cartesian3(0.0, 0.0, 0.0);
} // GeometricSurfaceFaceDS::GeometricSurfaceFaceDS()
// read routine returns true on success, failure otherwise
bool GeometricSurfaceFaceDS::ReadFileTriangleSoup(char *fileName)
{ // GeometricSurfaceFaceDS::ReadFileTriangleSoup()
// these are for accumulating a bounding box for the object
Cartesian3 minCoords(1000000.0, 1000000.0, 1000000.0);
Cartesian3 maxCoords(-1000000.0, -1000000.0, -1000000.0);
// open the input file
std::ifstream inFile(fileName);
if (inFile.bad())
return false;
// set the number of vertices and faces
long nTriangles = 0, nVertices = 0;
// set the midpoint to the origin
midPoint = Cartesian3(0.0, 0.0, 0.0);
// read in the number of vertices
inFile >> nTriangles;
nVertices = nTriangles * 3;
// now allocate space for them all
vertices.resize(nVertices);
// now loop to read the vertices in, and hope nothing goes wrong
for (int vertex = 0; vertex < nVertices; vertex++)
{ // for each vertex
inFile >> vertices[vertex].x >> vertices[vertex].y >> vertices[vertex].z;
// keep running track of midpoint, &c.
midPoint = midPoint + vertices[vertex];
if (vertices[vertex].x < minCoords.x) minCoords.x = vertices[vertex].x;
if (vertices[vertex].y < minCoords.y) minCoords.y = vertices[vertex].y;
if (vertices[vertex].z < minCoords.z) minCoords.z = vertices[vertex].z;
if (vertices[vertex].x > maxCoords.x) maxCoords.x = vertices[vertex].x;
if (vertices[vertex].y > maxCoords.y) maxCoords.y = vertices[vertex].y;
if (vertices[vertex].z > maxCoords.z) maxCoords.z = vertices[vertex].z;
} // for each vertex
// now sort out the size of a bounding sphere for viewing
// and also set the midpoint’s location
midPoint = midPoint / vertices.size();
// now go back through the vertices, subtracting the mid point
for (int vertex = 0; vertex < nVertices; vertex++)
{ // per vertex
vertices[vertex] = vertices[vertex] - midPoint;
} // per vertex
// the bounding sphere radius is just half the distance between these
boundingSphereSize = sqrt((maxCoords - minCoords).length()) * 1.0;
return true;
} // GeometricSurfaceFaceDS::ReadFileTriangleSoup()
// routine to render
void GeometricSurfaceFaceDS::Render()
{ // GeometricSurfaceFaceDS::Render()
// walk through the faces rendering each one
glBegin(GL_TRIANGLES);
// we will loop in 3's, assuming CCW order
for (unsigned int vertex = 0; vertex < vertices.size(); )
{ // per triangle
// use increment to step through them
Cartesian3 *v0 = &(vertices[vertex++]);
Cartesian3 *v1 = &(vertices[vertex++]);
Cartesian3 *v2 = &(vertices[vertex++]);
// now compute the normal vector
Cartesian3 uVec = *v1 - *v0;
Cartesian3 vVec = *v2 - *v0;
Cartesian3 normal = uVec.cross(vVec).normalise();
glNormal3fv(&normal.x);
glVertex3fv(&v0->x);
glVertex3fv(&v1->x);
glVertex3fv(&v2->x);
} // per triangle
glEnd();
} // GeometricSurfaceFaceDS::Render()
triangle_renderer/GeometricWidget.cpp
triangle_renderer/GeometricWidget.cpp///////////////////////////////////////////////////
//
// Hamish Carr
// January, 2018
//
// ————————
// GeometricWidget.h
// ————————
//
// The main widget that shows the geometry
//
///////////////////////////////////////////////////
#include
#ifdef __APPLE__
#include
#include
#else
#include
#include
#endif
#include “GeometricWidget.h”
static GLfloat light_position[] = {0.0, 0.0, 1.0, 0.0};
// constructor
GeometricWidget::GeometricWidget(GeometricSurfaceFaceDS *newSurface, QWidget *parent)
: QGLWidget(parent)
{ // constructor
// store pointer to the model
surface = newSurface;
// initialise arcballs to 80% of the widget’s size
Ball_Init(&lightBall); Ball_Place(&lightBall, qOne, 0.80);
Ball_Init(&objectBall); Ball_Place(&objectBall, qOne, 0.80);
// initialise translation values
translate_x = translate_y = 0.0;
// and set the button to an arbitrary value
whichButton = -1;
} // constructor
// destructor
GeometricWidget::~GeometricWidget()
{ // destructor
// nothing yet
} // destructor
// called when OpenGL context is set up
void GeometricWidget::initializeGL()
{ // GeometricWidget::initializeGL()
// enable Z-buffering
glEnable(GL_DEPTH_TEST);
// set lighting parameters
glShadeModel(GL_FLAT);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
// background is pink
glClearColor(1.0, 0.7, 0.7, 1.0);
} // GeometricWidget::initializeGL()
// called every time the widget is resized
void GeometricWidget::resizeGL(int w, int h)
{ // GeometricWidget::resizeGL()
// reset the viewport
glViewport(0, 0, w, h);
// set projection matrix to be glOrtho based on zoom & window size
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// retrieve the scale factor
float size = surface->boundingSphereSize;
// compute the aspect ratio of the widget
float aspectRatio = (float) w / (float) h;
// depending on aspect ratio, set to accomodate a sphere of radius = diagonal without clipping
if (aspectRatio > 1.0)
glOrtho(-aspectRatio * size, aspectRatio * size, -size, size, -size, size);
else
glOrtho(-size, size, -size/aspectRatio, size/aspectRatio, -size, size);
} // GeometricWidget::resizeGL()
// called every time the widget needs painting
void GeometricWidget::paintGL()
{ // GeometricWidget::paintGL()
// clear the buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// set lighting on
glEnable(GL_LIGHTING);
// set model view matrix based on stored translation, rotation &c.
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// set light position first
// retrieve rotation from arcball & apply
GLfloat mNow[16];
Ball_Value(&lightBall, mNow);
glMultMatrixf(mNow);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
// apply translation for interface control
glLoadIdentity();
glTranslatef(translate_x, translate_y, 0.0);
// apply rotation matrix from arcball
Ball_Value(&objectBall, mNow);
glMultMatrixf(mNow);
// now draw the surface
surface->Render();
} // GeometricWidget::paintGL()
// mouse-handling
void GeometricWidget::mousePressEvent(QMouseEvent *event)
{ // GeometricWidget::mousePressEvent()
// store the button for future reference
whichButton = event->button();
// find the minimum of height & width
float size = (width() > height()) ? height() : width();
// convert to the ArcBall’s vector type
HVect vNow;
// scale both coordinates from that
vNow.x = (2.0 * event->x() – size) / size;
vNow.y = (size – 2.0 * event->y() ) / size;
// now either translate or rotate object or light
switch(whichButton)
{ // button switch
case Qt::RightButton:
// save the last x, y
last_x = vNow.x; last_y = vNow.y;
// and update
updateGL();
break;
case Qt::MiddleButton:
// pass the point to the arcball code
Ball_Mouse(&lightBall, vNow);
// start dragging
Ball_BeginDrag(&lightBall);
// update the widget
updateGL();
break;
case Qt::LeftButton:
// pass the point to the arcball code
Ball_Mouse(&objectBall, vNow);
// start dragging
Ball_BeginDrag(&objectBall);
// update the widget
updateGL();
break;
} // button switch
} // GeometricWidget::mousePressEvent()
void GeometricWidget::mouseMoveEvent(QMouseEvent *event)
{ // GeometricWidget::mouseMoveEvent()
// find the minimum of height & width
float size = (width() > height()) ? height() : width();
// convert to the ArcBall’s vector type
HVect vNow;
// scale both coordinates from that
vNow.x = (2.0 * event->x() – size) / size;
vNow.y = (size – 2.0 * event->y() ) / size;
// now either translate or rotate object or light
switch(whichButton)
{ // button switch
case Qt::RightButton:
// subtract the translation
translate_x += vNow.x – last_x;
translate_y += vNow.y – last_y;
last_x = vNow.x;
last_y = vNow.y;
// update the widget
updateGL();
break;
case Qt::MiddleButton:
// pass it to the arcball code
Ball_Mouse(&lightBall, vNow);
// start dragging
Ball_Update(&lightBall);
// update the widget
updateGL();
break;
case Qt::LeftButton:
// pass it to the arcball code
Ball_Mouse(&objectBall, vNow);
// start dragging
Ball_Update(&objectBall);
// update the widget
updateGL();
break;
} // button switch
} // GeometricWidget::mouseMoveEvent()
void GeometricWidget::mouseReleaseEvent(QMouseEvent *event)
{ // GeometricWidget::mouseReleaseEvent()
// now either translate or rotate object or light
switch(whichButton)
{ // button switch
case Qt::RightButton:
// just update
updateGL();
break;
case Qt::MiddleButton:
// end the drag
Ball_EndDrag(&lightBall);
// update the widget
updateGL();
break;
case Qt::LeftButton:
// end the drag
Ball_EndDrag(&objectBall);
// update the widget
updateGL();
break;
} // button switch
} // GeometricWidget::mouseReleaseEvent()
triangle_renderer/main.cpp
triangle_renderer/main.cpp///////////////////////////////////////////////////
//
// Hamish Carr
// January, 2018
//
// ————————
// main.cpp
// ————————
//
///////////////////////////////////////////////////
#include
#include “GeometricWidget.h”
#include
int main(int argc, char **argv)
{ // main()
// initialize QT
QApplication app(argc, argv);
// the geometric surface
GeometricSurfaceFaceDS surface;
// check the args to make sure there’s an input file
if (argc == 2)
{ // two parameters – read a file
if (!surface.ReadFileTriangleSoup(argv[1]))
{ // surface read failed
printf(“Read failed for file %s\n”, argv[1]);
exit(0);
} // surface read failed
else
{ // surface read succeeded
// create a window
GeometricWidget aWindow(&surface, NULL);
// set the initial size
aWindow.resize(600, 600);
// show the window
aWindow.show();
// set QT running
return app.exec();
} // surface read succeeded
} // two parameters – read a file
else
{ // too many parameters
printf(“Usage: %s filename\n”, argv[0]);
exit (0);
} // too many parameters
// paranoid return value
exit(0);
} // main()
triangle_renderer/BallMath.cpp
triangle_renderer/BallMath.cpp/**** BallMath.c – Essential routines for ArcBall. ****/
#include
#include “BallMath.h”
#include “BallAux.h”
/* Convert window coordinates to sphere coordinates. */
HVect MouseOnSphere(HVect mouse, HVect ballCenter, double ballRadius)
{
HVect ballMouse;
double mag;
ballMouse.x = (mouse.x – ballCenter.x) / ballRadius;
ballMouse.y = (mouse.y – ballCenter.y) / ballRadius;
mag = ballMouse.x*ballMouse.x + ballMouse.y*ballMouse.y;
if (mag > 1.0) {
double scale = 1.0/sqrt(mag);
ballMouse.x *= scale; ballMouse.y *= scale;
ballMouse.z = 0.0;
} else {
ballMouse.z = sqrt(1 – mag);
}
ballMouse.w = 0.0;
return (ballMouse);
}
/* Construct a unit quaternion from two points on unit sphere */
Quat Qt_FromBallPoints(HVect from, HVect to)
{
Quat qu;
qu.x = from.y*to.z – from.z*to.y;
qu.y = from.z*to.x – from.x*to.z;
qu.z = from.x*to.y – from.y*to.x;
qu.w = from.x*to.x + from.y*to.y + from.z*to.z;
return (qu);
}
/* Convert a unit quaternion to two points on unit sphere */
void Qt_ToBallPoints(Quat q, HVect *arcFrom, HVect *arcTo)
{
double s;
s = sqrt(q.x*q.x + q.y*q.y);
if (s == 0.0) {
*arcFrom = V3_(0.0, 1.0, 0.0);
} else {
*arcFrom = V3_(-q.y/s, q.x/s, 0.0);
}
arcTo->x = q.w*arcFrom->x – q.z*arcFrom->y;
arcTo->y = q.w*arcFrom->y + q.z*arcFrom->x;
arcTo->z = q.x*arcFrom->y – q.y*arcFrom->x;
if (q.w < 0.0) *arcFrom = V3_(-arcFrom->x, -arcFrom->y, 0.0);
}
/* Force sphere point to be perpendicular to axis. */
HVect ConstrainToAxis(HVect loose, HVect axis)
{
HVect onPlane;
float norm;
onPlane = V3_Sub(loose, V3_Scale(axis, V3_Dot(axis, loose)));
norm = V3_Norm(onPlane);
if (norm > 0.0) {
if (onPlane.z < 0.0) onPlane = V3_Negate(onPlane);
return ( V3_Scale(onPlane, 1/sqrt(norm)) );
} /* else drop through */
if (axis.z == 1) {
onPlane = V3_(1.0, 0.0, 0.0);
} else {
onPlane = V3_Unit(V3_(-axis.y, axis.x, 0.0));
}
return (onPlane);
}
/* Find the index of nearest arc of axis set. */
int NearestConstraintAxis(HVect loose, HVect *axes, int nAxes)
{
HVect onPlane;
float max, dot;
int i, nearest;
max = -1; nearest = 0;
for (i=0; i
max = dot; nearest = i;
}
}
return (nearest);
}
///////////////////////////////////////////////////
//
// Hamish Carr
// January, 2018
//
// ————————
// GeometricWidget.h
// ————————
//
// The main widget that shows the geometry
//
///////////////////////////////////////////////////
#ifndef _GEOMETRIC_WIDGET_H
#define _GEOMETRIC_WIDGET_H
#include
#include
#include “GeometricSurfaceFaceDS.h”
#include “Ball.h”
class GeometricWidget : public QGLWidget
{ // class GeometricWidget
Q_OBJECT
public:
// the model – i.e. the surface
GeometricSurfaceFaceDS *surface;
// arcball for storing light rotation
BallData lightBall;
// arcball for storing object rotation
BallData objectBall;
// translation in window x,y
GLfloat translate_x, translate_y;
GLfloat last_x, last_y;
// which button was last pressed
int whichButton;
// constructor
GeometricWidget(GeometricSurfaceFaceDS *newSurface, QWidget *parent);
// destructor
~GeometricWidget();
protected:
// called when OpenGL context is set up
void initializeGL();
// called every time the widget is resized
void resizeGL(int w, int h);
// called every time the widget needs painting
void paintGL();
// mouse-handling
virtual void mousePressEvent(QMouseEvent *event);
virtual void mouseMoveEvent(QMouseEvent *event);
virtual void mouseReleaseEvent(QMouseEvent *event);
}; // class GeometricWidget
#endif
triangle_renderer/Ball.cpp
triangle_renderer/Ball.cpp/***** Ball.c *****/
/* Ken Shoemake, 1993 */
#ifdef __APPLE__
#include
#else
#include
#endif
#include “Ball.h”
#include “BallMath.h”
#include “BallAux.h”
#define ARCBALL_TRUE 1
#define ARCBALL_FALSE 0
#define LG_NSEGS 4
#define NSEGS (1<
ball->radius = 1.0;
ball->vDown = ball->vNow = qOne;
ball->qDown = ball->qNow = qOne;
for (i=15; i>=0; i–)
((float *)ball->mNow)[i] = ((float *)ball->mDown)[i] = ((float *)mId)[i];
ball->showResult = ball->dragging = ARCBALL_FALSE;
ball->axisSet = NoAxes;
ball->sets[CameraAxes] = mId[X]; ball->setSizes[CameraAxes] = 3;
ball->sets[BodyAxes] = ball->mDown[X]; ball->setSizes[BodyAxes] = 3;
ball->sets[OtherAxes] = otherAxis[X]; ball->setSizes[OtherAxes] = 1;
}
/* Set the center and size of the controller. */
void Ball_Place(BallData *ball, HVect center, double radius)
{
ball->center = center;
ball->radius = radius;
}
/* Incorporate new mouse position. */
void Ball_Mouse(BallData *ball, HVect vNow)
{
ball->vNow = vNow;
}
/* Choose a constraint set, or none. */
void Ball_UseSet(BallData *ball, AxisSet axisSet)
{
if (!ball->dragging) ball->axisSet = axisSet;
}
/* Begin drawing arc for all drags combined. */
void Ball_ShowResult(BallData *ball)
{
ball->showResult = ARCBALL_TRUE;
}
/* Stop drawing arc for all drags combined. */
void Ball_HideResult(BallData *ball)
{
ball->showResult = ARCBALL_FALSE;
}
/* Using vDown, vNow, dragging, and axisSet, compute rotation etc. */
void Ball_Update(BallData *ball)
{
int setSize = ball->setSizes[ball->axisSet];
HVect *set = (HVect *)(ball->sets[ball->axisSet]);
ball->vFrom = MouseOnSphere(ball->vDown, ball->center, ball->radius);
ball->vTo = MouseOnSphere(ball->vNow, ball->center, ball->radius);
if (ball->dragging) {
if (ball->axisSet!=NoAxes) {
ball->vFrom = ConstrainToAxis(ball->vFrom, set[ball->axisIndex]);
ball->vTo = ConstrainToAxis(ball->vTo, set[ball->axisIndex]);
}
ball->qDrag = Qt_FromBallPoints(ball->vFrom, ball->vTo);
ball->qNow = Qt_Mul(ball->qDrag, ball->qDown);
} else {
if (ball->axisSet!=NoAxes) {
ball->axisIndex = NearestConstraintAxis(ball->vTo, set, setSize);
}
}
Qt_ToBallPoints(ball->qDown, &ball->vrFrom, &ball->vrTo);
Qt_ToMatrix(Qt_Conj(ball->qNow), ball->mNow); /* Gives transpose for GL. */
}
/* Return rotation matrix defined by controller use. */
void Ball_Value(BallData *ball, GLfloat *mNow)
{
int i;
for (i=15; i>=0; i–) mNow[i] = ((float *)ball->mNow)[i];
}
/* Begin drag sequence. */
void Ball_BeginDrag(BallData *ball)
{
ball->dragging = ARCBALL_TRUE;
ball->vDown = ball->vNow;
}
/* Stop drag sequence. */
void Ball_EndDrag(BallData *ball)
{
int i;
ball->dragging = ARCBALL_FALSE;
ball->qDown = ball->qNow;
for (i=15; i>=0; i–)
((float *)ball->mDown)[i] = ((float *)ball->mNow)[i];
}
/* Draw the controller with all its arcs. */
void Ball_Draw(BallData *ball)
{
float r = ball->radius;
glPushMatrix();
glLoadIdentity();
glOrtho(-1.0, 1.0, -1.0, 1.0, 1.0, 1.0);
RIMCOLOR();
glScalef(r, r, r);
/* circ(0.0, 0.0, 1.0); */
Ball_DrawResultArc(ball);
Ball_DrawConstraints(ball);
Ball_DrawDragArc(ball);
glPopMatrix();
}
/* Draw an arc defined by its ends. */
void DrawAnyArc(HVect vFrom, HVect vTo)
{
int i;
HVect pts[NSEGS+1];
double dot;
pts[0] = vFrom;
pts[1] = pts[NSEGS] = vTo;
for (i=0; i
if (ball->axisSet==NoAxes) return;
set = ball->sets[ball->axisSet];
for (axisI=0; axisI
if (ball->dragging) continue;
FARCOLOR();
} else NEARCOLOR();
axis = *(HVect *)&set[4*axisI];
/* if (axis.z==1.0) {
circ(0.0, 0.0, 1.0);
} else {
*/ DrawHalfArc(axis);
/* } */
}
}
/* Draw “rubber band” arc during dragging. */
void Ball_DrawDragArc(BallData *ball)
{
DRAGCOLOR();
if (ball->dragging) DrawAnyArc(ball->vFrom, ball->vTo);
}
/* Draw arc for result of all drags. */
void Ball_DrawResultArc(BallData *ball)
{
RESCOLOR();
if (ball->showResult) DrawAnyArc(ball->vrFrom, ball->vrTo);
}
._triangle_renderer
triangle_renderer/BallMath.h
triangle_renderer/BallAux.cpp
triangle_renderer/._Ball.h
triangle_renderer/Ball.h
triangle_renderer/GeometricSurfaceFaceDS.h
triangle_renderer/Cartesian3.h
triangle_renderer/BallAux.h
triangle_renderer/._readme.txt
triangle_renderer/readme.txt
triangle_renderer/Cartesian3.cpp
triangle_renderer/GeometricSurfaceFaceDS.cpp
triangle_renderer/GeometricWidget.cpp
triangle_renderer/main.cpp
triangle_renderer/._BallMath.cpp
triangle_renderer/BallMath.cpp
triangle_renderer/GeometricWidget.h
triangle_renderer/._Ball.cpp
triangle_renderer/Ball.cpp