5
// Created by Jens Ayton on 2009-11-18.
6
// Copyright 2009 Jens Ayton. All rights reserved.
9
#import "JAIcosTriangle.h"
12
static inline BOOL IsPolarVector(Vector v)
14
return v.x == 0.0 && v.z == 0.0;
18
@interface JAIcosTriangle ()
20
- (void) rotate; // a = b, b = c, c = a
21
- (void) generateTextureCoordinatesAndBinormals; // Requires that any polar coordinate is in [0].
22
- (void) fixUpWinding;
27
static NSComparisonResult CompareVertices(Vertex *a, Vertex *b)
33
static NSString *VertexDescription(Vertex v)
35
return [NSString stringWithFormat:@"{ %g, %g, %g} (%g, %g)", v.v.x, v.v.y, v.v.z, v.s, v.t];
39
@implementation JAIcosTriangle
41
+ (id) triangleWithVectorA:(Vector)a b:(Vector)b c:(Vector)c
43
return [[self alloc] initWithVectorA:a b:b c:c];
47
- (id) initWithVectorA:(Vector)a b:(Vector)b c:(Vector)c
49
if ((self = [super init]))
51
_vertices[0].v = VectorNormal(a);
52
_vertices[1].v = VectorNormal(b);
53
_vertices[2].v = VectorNormal(c);
55
// If one of our vertices is a pole, make it the first.
56
if (IsPolarVector(_vertices[2].v)) [self rotate];
57
if (IsPolarVector(_vertices[1].v)) [self rotate];
59
// Ensure winding is consistent.
62
[self generateTextureCoordinatesAndBinormals];
69
- (NSString *) description
71
return [NSString stringWithFormat:@"{%@, %@, %@}", VertexDescription(_vertices[0]), VertexDescription(_vertices[1]), VertexDescription(_vertices[2])];
95
Vertex temp = _vertices[0];
96
_vertices[0] = _vertices[1];
97
_vertices[1] = _vertices[2];
102
static inline Vector BinormalFromNormal(Vector v)
104
return VectorNormal(VectorCross(v, (Vector){0, 1.0, 0.0}));
108
- (void) generateTextureCoordinatesAndBinormals
110
VectorToCoords0_1(_vertices[1].v, &_vertices[1].t, &_vertices[1].s);
111
VectorToCoords0_1(_vertices[2].v, &_vertices[2].t, &_vertices[2].s);
112
if (!IsPolarVector(_vertices[0].v)) VectorToCoords0_1(_vertices[0].v, &_vertices[0].t, &_vertices[0].s);
115
// Use longitude of average of v1 and v2.
116
VectorToCoords0_1(VectorAdd(_vertices[1].v, _vertices[2].v), NULL, &_vertices[0].s);
117
_vertices[0].t = (_vertices[0].v.y < 0) ? 1.0 : 0.0;
120
/* Texture seam handling
121
At the back of the mesh, at the longitude = 180°/-180° meridian, the
122
texture wraps around. However, there isn't a convenient matching seam
123
in the geometry - there are no great circles touching "poles" (source
124
vertices) on a subdivided icosahedron - so we need to adjust texture
125
coordinates and use the GL_REPEAT texture wrapping mode to cover it
128
The technique is to establish whether we have at least one vertex in
129
each of the (x, -z) and (-x, -z) quadrants, and if so, add 1 to the
130
texture coordinates for the vertices in (-x, -z) -- corresponding to
133
NOTE: this technique is suboptimal because the selection of wrapped
134
vertices changes at each subdivision level. Interpolating texture
135
coordinates during subidivision, then finding the "nearest" option
136
for "correct" calculated s for interpolated vertices could fix this.
139
bool haveNXNZ = false;
140
bool havePXNZ = false;
142
for (i = 0; i < 3; i++)
144
if (_vertices[i].v.z < 0)
146
if (_vertices[i].v.x < 0)
157
if (haveNXNZ && havePXNZ)
159
for (i = 0; i < 3; i++)
161
if (_vertices[i].v.z < 0 && _vertices[i].v.x >= 0)
163
// printf("Remapping %g -> %g\n", _vertices[i].s, _vertices[i].s + 1.0);
164
_vertices[i].s += 1.0;
167
if (_vertices[0].v.y == -1.0)
169
/* Special case: also need to wrap the polar coordinate of the
170
southernmost seam triangle.
172
_vertices[0].s += 1.0;
178
For non-polar points, the binormal is the cross product of the normal
179
(equal to the vertex, for a unit sphere) and the Y axis. At the poles,
180
this is a singularity and we use the average of the other two binormals
183
Note: for non-polar vertices, it makes sense to calculate this in the
184
shaders instead. It may be better to use texture coordinates to handle
185
polar coordinates instead of using an attribute.
187
_vertices[1].binormal = BinormalFromNormal(_vertices[1].v);
188
_vertices[2].binormal = BinormalFromNormal(_vertices[2].v);
189
if (!IsPolarVector(_vertices[0].v)) _vertices[0].binormal = BinormalFromNormal(_vertices[0].v);
192
_vertices[0].binormal = VectorNormal(VectorAdd(_vertices[1].binormal, _vertices[2].binormal));
198
- (NSArray *) subdivide
200
Vector a = _vertices[0].v;
201
Vector b = _vertices[1].v;
202
Vector c = _vertices[2].v;
204
Vector ab = VectorNormal(VectorAdd(a, b));
205
Vector bc = VectorNormal(VectorAdd(b, c));
206
Vector ca = VectorNormal(VectorAdd(c, a));
208
/* Note: vertex orders preserve winding. Triangle order is intended to be
209
somewhat cache-friendly, but not as good as actually optimizing the
212
JAIcosTriangle *subTris[4];
213
subTris[0] = [JAIcosTriangle triangleWithVectorA:a b:ab c:ca];
214
subTris[3] = [JAIcosTriangle triangleWithVectorA:ab b:bc c:ca];
215
subTris[1] = [JAIcosTriangle triangleWithVectorA:ab b:b c:bc];
216
subTris[2] = [JAIcosTriangle triangleWithVectorA:ca b:bc c:c];
218
return [NSArray arrayWithObjects:subTris count:4];
222
- (void) fixUpWinding
224
// Construct vectors from v0 to v1 and v0 to v2.
225
Vector ab = VectorSubtract(_vertices[1].v, _vertices[0].v);
226
Vector ac = VectorSubtract(_vertices[2].v, _vertices[0].v);
228
// Take their cross product.
229
Vector cross = VectorCross(ab, ac);
231
// For a correctly-wound triangle, this should point inwards.
232
// Since our corner vectors point generally outwards, the dot product of
233
// any of these with cross should be negative.
234
if (VectorDot(cross, _vertices[0].v) > 0)
236
// If not, we swap v1 and v2.
237
Vertex temp = _vertices[1];
238
_vertices[1] = _vertices[2];