635 lines
18 KiB
C
635 lines
18 KiB
C
/*
|
|
* JoeyLib 3D
|
|
* Copyright (C) 2019 Scott Duensing <scott@kangaroopunch.com>
|
|
*
|
|
* This software is provided 'as-is', without any express or implied
|
|
* warranty. In no event will the authors be held liable for any damages
|
|
* arising from the use of this software.
|
|
*
|
|
* Permission is granted to anyone to use this software for any purpose,
|
|
* including commercial applications, and to alter it and redistribute it
|
|
* freely, subject to the following restrictions:
|
|
*
|
|
* 1. The origin of this software must not be misrepresented; you must not
|
|
* claim that you wrote the original software. If you use this software
|
|
* in a product, an acknowledgment in the product documentation would be
|
|
* appreciated but is not required.
|
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
* misrepresented as being the original software.
|
|
* 3. This notice may not be removed or altered from any source distribution.
|
|
*/
|
|
|
|
// The code in this file is heavily influenced by
|
|
// "Black Art of 3D Game Programming" by Andre LaMothe
|
|
// Waite Group Press - ISBN 1-57169-004-2
|
|
// https://archive.org/details/BlackArt3DEBook
|
|
|
|
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <float.h>
|
|
#include <string.h>
|
|
|
|
#include "stretchy_buffer.h"
|
|
|
|
#include "j3d.h"
|
|
|
|
|
|
#ifdef JOEY_IIGS
|
|
|
|
segment "j3d";
|
|
#define M_PI 3.1415926
|
|
#define fabsf fabs
|
|
|
|
#else
|
|
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wpadded"
|
|
|
|
#endif
|
|
|
|
|
|
// Module global data
|
|
static float sin_table[361];
|
|
static float cos_table[361];
|
|
static int clipMinX = 0;
|
|
static int clipMinY = 0;
|
|
static int clipMaxX = 319;
|
|
static int clipMaxY = 199;
|
|
static int viewDistance = 250;
|
|
|
|
|
|
#define ASPECT_RATIO (float)0.8
|
|
#define INVERSE_ASPECT_RATIO (float)1.25
|
|
|
|
#define HALF_SCREEN_WIDTH 160
|
|
#define HALF_SCREEN_HEIGHT 100
|
|
|
|
|
|
void _j3DrawWireframePair(j3ObjectT *o, int v1, int v2) {
|
|
float x1;
|
|
float y1;
|
|
float z1;
|
|
float x2;
|
|
float y2;
|
|
float z2;
|
|
int ix1;
|
|
int iy1;
|
|
int ix2;
|
|
int iy2;
|
|
j3VertexT v;
|
|
|
|
v = o->verticies[v1].camera;
|
|
x1 = v.x;
|
|
y1 = v.y;
|
|
z1 = v.z;
|
|
|
|
v = o->verticies[v2].camera;
|
|
x2 = v.x;
|
|
y2 = v.y;
|
|
z2 = v.z;
|
|
|
|
x1 = HALF_SCREEN_WIDTH + x1 * viewDistance / z1;
|
|
y1 = HALF_SCREEN_HEIGHT - ASPECT_RATIO * y1 * viewDistance / z1;
|
|
|
|
x2 = HALF_SCREEN_WIDTH + x2 * viewDistance / z2;
|
|
y2 = HALF_SCREEN_HEIGHT - ASPECT_RATIO * y2 * viewDistance / z2;
|
|
|
|
ix1 = (int)x1;
|
|
iy1 = (int)y1;
|
|
ix2 = (int)x2;
|
|
iy2 = (int)y2;
|
|
|
|
if (_j3UtilClipLine(&ix1, &iy1, &ix2, &iy2)) {
|
|
jlDrawLine((jint16)ix1, (jint16)iy1, (jint16)ix2, (jint16)iy2);
|
|
}
|
|
}
|
|
|
|
|
|
void _j3DrawWireframe(j3ObjectT *o) {
|
|
int t; // Current triangle
|
|
|
|
for (t=0; t<o->triangleCount; t++) {
|
|
_j3DrawWireframePair(o, o->triangles[t].index[0], o->triangles[t].index[1]);
|
|
_j3DrawWireframePair(o, o->triangles[t].index[1], o->triangles[t].index[2]);
|
|
_j3DrawWireframePair(o, o->triangles[t].index[2], o->triangles[t].index[0]);
|
|
}
|
|
}
|
|
|
|
|
|
void j3MathMatrix4x4Identity(j3Matrix4x4T result) {
|
|
result[0][0] = 1; result[0][1] = 0; result[0][2] = 0; result[0][3] = 0;
|
|
result[1][0] = 0; result[1][1] = 1; result[1][2] = 0; result[1][3] = 0;
|
|
result[2][0] = 0; result[2][1] = 0; result[2][2] = 1; result[2][3] = 0;
|
|
result[3][0] = 0; result[3][1] = 0; result[3][2] = 0; result[3][3] = 1;
|
|
}
|
|
|
|
|
|
void j3MathMatrix4x4Mult(j3Matrix4x4T a, j3Matrix4x4T b, j3Matrix4x4T result) {
|
|
result[0][0] = a[0][0] * b[0][0] + a[0][1] * b[1][0] + a[0][2] * b[2][0];
|
|
result[0][1] = a[0][0] * b[0][1] + a[0][1] * b[1][1] + a[0][2] * b[2][1];
|
|
result[0][2] = a[0][0] * b[0][2] + a[0][1] * b[1][2] + a[0][2] * b[2][2];
|
|
result[0][3] = 0;
|
|
|
|
result[1][0] = a[1][0] * b[0][0] + a[1][1] * b[1][0] + a[1][2] * b[2][0];
|
|
result[1][1] = a[1][0] * b[0][1] + a[1][1] * b[1][1] + a[1][2] * b[2][1];
|
|
result[1][2] = a[1][0] * b[0][2] + a[1][1] * b[1][2] + a[1][2] * b[2][2];
|
|
result[1][3] = 0;
|
|
|
|
result[2][0] = a[2][0] * b[0][0] + a[2][1] * b[1][0] + a[2][2] * b[2][0];
|
|
result[2][1] = a[2][0] * b[0][1] + a[2][1] * b[1][1] + a[2][2] * b[2][1];
|
|
result[2][2] = a[2][0] * b[0][2] + a[2][1] * b[1][2] + a[2][2] * b[2][2];
|
|
result[2][3] = 0;
|
|
|
|
result[3][0] = a[3][0] * b[0][0] + a[3][1] * b[1][0] + a[3][2] * b[2][0] + b[3][0];
|
|
result[3][1] = a[3][0] * b[0][1] + a[3][1] * b[1][1] + a[3][2] * b[2][1] + b[3][1];
|
|
result[3][2] = a[3][0] * b[0][2] + a[3][1] * b[1][2] + a[3][2] * b[2][2] + b[3][2];
|
|
result[3][3] = 1;
|
|
}
|
|
|
|
|
|
void _j3ObjectReset(j3ObjectT *o) {
|
|
o->position.x = 0;
|
|
o->position.y = 0;
|
|
o->position.z = 0;
|
|
|
|
o->rotation.x = 0;
|
|
o->rotation.y = 0;
|
|
o->rotation.z = 0;
|
|
|
|
o->scale.x = 1;
|
|
o->scale.y = 1;
|
|
o->scale.z = 1;
|
|
}
|
|
|
|
|
|
void _j3ObjectUpdate(j3ObjectT *o) {
|
|
int x;
|
|
int y;
|
|
int z;
|
|
int i;
|
|
int axis = 0;
|
|
j3Matrix4x4T rotateX;
|
|
j3Matrix4x4T rotateY;
|
|
j3Matrix4x4T rotateZ;
|
|
j3Matrix4x4T final;
|
|
j3Matrix4x4T temp;
|
|
|
|
// === ROTATION ===
|
|
|
|
x = (int)o->rotation.x;
|
|
y = (int)o->rotation.y;
|
|
z = (int)o->rotation.z;
|
|
|
|
j3MathMatrix4x4Identity(final);
|
|
|
|
// What angles are we rotating on? By knowing this we can optimize some.
|
|
if (x) axis += 4;
|
|
if (y) axis += 2;
|
|
if (z) axis += 1;
|
|
|
|
switch (axis) {
|
|
case 1: // Final matrix = z
|
|
final[0][0] = cos_table[z];
|
|
final[0][1] = sin_table[z];
|
|
final[1][0] = -sin_table[z];
|
|
final[1][1] = cos_table[z];
|
|
for (i=0; i<o->vertexCount; i++) {
|
|
o->verticies[i].world.x = o->verticies[i].local.x * final[0][0] + o->verticies[i].local.y * final[1][0];
|
|
o->verticies[i].world.y = o->verticies[i].local.x * final[0][1] + o->verticies[i].local.y * final[1][1];
|
|
o->verticies[i].world.z = o->verticies[i].local.z;
|
|
}
|
|
break;
|
|
|
|
case 2: // Final matrix = y
|
|
final[0][0] = cos_table[y];
|
|
final[0][2] = -sin_table[y];
|
|
final[2][0] = sin_table[y];
|
|
final[2][2] = cos_table[y];
|
|
for (i=0; i<o->vertexCount; i++) {
|
|
o->verticies[i].world.x = o->verticies[i].local.x * final[0][0] + o->verticies[i].local.z * final[2][0];
|
|
o->verticies[i].world.y = o->verticies[i].local.y;
|
|
o->verticies[i].world.z = o->verticies[i].local.x * final[0][2] + o->verticies[i].local.z * final[2][2];
|
|
}
|
|
break;
|
|
|
|
case 3: // Final matrix = y * z
|
|
final[0][0] = cos_table[y] * cos_table[z];
|
|
final[0][1] = cos_table[y] * sin_table[z];
|
|
final[0][2] = -sin_table[y];
|
|
|
|
final[1][0] = -sin_table[z];
|
|
final[1][1] = cos_table[z];
|
|
|
|
final[2][0] = sin_table[y] * cos_table[z];
|
|
final[2][1] = sin_table[y] * sin_table[z];
|
|
final[2][2] = cos_table[y];
|
|
|
|
for (i=0; i<o->vertexCount; i++) {
|
|
o->verticies[i].world.x = o->verticies[i].local.x * final[0][0] + o->verticies[i].local.y * final[1][0] + o->verticies[i].local.z * final[2][0];
|
|
o->verticies[i].world.y = o->verticies[i].local.x * final[0][1] + o->verticies[i].local.y * final[1][1] + o->verticies[i].local.z * final[2][1];
|
|
o->verticies[i].world.z = o->verticies[i].local.x * final[0][2] + o->verticies[i].local.z * final[2][2];
|
|
}
|
|
break;
|
|
|
|
case 4: // Final matrix = x
|
|
final[1][1] = cos_table[x];
|
|
final[1][2] = sin_table[x];
|
|
final[2][1] = -sin_table[x];
|
|
final[2][2] = cos_table[x];
|
|
for (i=0; i<o->vertexCount; i++) {
|
|
o->verticies[i].world.x = o->verticies[i].local.x;
|
|
o->verticies[i].world.y = o->verticies[i].local.y * final[1][1] + o->verticies[i].local.z * final[2][1];
|
|
o->verticies[i].world.z = o->verticies[i].local.y * final[1][2] + o->verticies[i].local.z * final[2][2];
|
|
}
|
|
break;
|
|
|
|
case 5: // Final matrix = x * z
|
|
final[0][0] = cos_table[z];
|
|
final[0][1] = sin_table[z];
|
|
|
|
final[1][0] = -cos_table[x]*sin_table[z];
|
|
final[1][1] = cos_table[x]*cos_table[z];
|
|
final[1][2] = sin_table[x];
|
|
|
|
final[2][0] = sin_table[x]*sin_table[z];
|
|
final[2][1] = -sin_table[x]*cos_table[z];
|
|
final[2][2] = cos_table[x];
|
|
|
|
for (i=0; i<o->vertexCount; i++) {
|
|
o->verticies[i].world.x = o->verticies[i].local.x * final[0][0] + o->verticies[i].local.y * final[1][0] + o->verticies[i].local.z * final[2][0];
|
|
o->verticies[i].world.y = o->verticies[i].local.x * final[0][1] + o->verticies[i].local.y * final[1][1] + o->verticies[i].local.z * final[2][1];
|
|
o->verticies[i].world.z = o->verticies[i].local.y * final[1][2] + o->verticies[i].local.z * final[2][2];
|
|
}
|
|
break;
|
|
|
|
case 6: // Final matrix = x * y
|
|
final[0][0] = cos_table[y];
|
|
final[0][2] = -sin_table[y];
|
|
|
|
final[1][0] = sin_table[x] * sin_table[y];
|
|
final[1][1] = cos_table[x];
|
|
final[1][2] = sin_table[x] * cos_table[y];
|
|
|
|
final[2][0] = cos_table[x] * sin_table[y];
|
|
final[2][1] = -sin_table[x];
|
|
final[2][2] = cos_table[x] * cos_table[y];
|
|
|
|
for (i=0; i<o->vertexCount; i++) {
|
|
o->verticies[i].world.x = o->verticies[i].local.x * final[0][0] + o->verticies[i].local.y * final[1][0] + o->verticies[i].local.z * final[2][0];
|
|
o->verticies[i].world.y = o->verticies[i].local.y * final[1][1] + o->verticies[i].local.z * final[2][1];
|
|
o->verticies[i].world.z = o->verticies[i].local.x * final[0][2] + o->verticies[i].local.y * final[1][2] + o->verticies[i].local.z * final[2][2];
|
|
}
|
|
break;
|
|
|
|
case 7: // Final matrix = x * y * z
|
|
j3MathMatrix4x4Identity(rotateX);
|
|
rotateX[1][1] = cos_table[x];
|
|
rotateX[1][2] = sin_table[x];
|
|
rotateX[2][1] = -sin_table[x];
|
|
rotateX[2][2] = cos_table[x];
|
|
|
|
j3MathMatrix4x4Identity(rotateY);
|
|
rotateY[0][0] = cos_table[y];
|
|
rotateY[0][2] = -sin_table[y];
|
|
rotateY[2][0] = sin_table[y];
|
|
rotateY[2][2] = cos_table[y];
|
|
|
|
j3MathMatrix4x4Identity(rotateZ);
|
|
rotateZ[0][0] = cos_table[z];
|
|
rotateZ[0][1] = sin_table[z];
|
|
rotateZ[1][0] = -sin_table[z];
|
|
rotateZ[1][1] = cos_table[z];
|
|
|
|
j3MathMatrix4x4Mult(rotateX, rotateZ, temp);
|
|
j3MathMatrix4x4Mult(temp, rotateZ, final);
|
|
|
|
for (i=0; i<o->vertexCount; i++) {
|
|
o->verticies[i].world.x = o->verticies[i].local.x * final[0][0] + o->verticies[i].local.y * final[1][0] + o->verticies[i].local.z * final[2][0];
|
|
o->verticies[i].world.y = o->verticies[i].local.x * final[0][1] + o->verticies[i].local.y * final[1][1] + o->verticies[i].local.z * final[2][1];
|
|
o->verticies[i].world.z = o->verticies[i].local.x * final[0][2] + o->verticies[i].local.y * final[1][2] + o->verticies[i].local.z * final[2][2];
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// === SCALE & TRANSLATION ===
|
|
|
|
for (i=0; i<o->vertexCount; i++) {
|
|
o->verticies[i].world.x = o->verticies[i].world.x * o->scale.x + o->position.x;
|
|
o->verticies[i].world.y = o->verticies[i].world.y * o->scale.y + o->position.y;
|
|
o->verticies[i].world.z = o->verticies[i].world.z * o->scale.z + o->position.z;
|
|
}
|
|
|
|
// === CAMERA SPACE ===
|
|
//***TODO*** Move this?
|
|
|
|
for (i=0; i<o->vertexCount; i++) {
|
|
o->verticies[i].camera = o->verticies[i].world;
|
|
}
|
|
}
|
|
|
|
|
|
bool _j3UtilClipLine(int *x1, int *y1, int *x2, int *y2) {
|
|
int xi;
|
|
int yi;
|
|
float dx;
|
|
float dy;
|
|
bool point1 = false; // End points visible?
|
|
bool point2 = false;
|
|
bool rightEdge = false; // Which edges are the endpoints beyond?
|
|
bool leftEdge = false;
|
|
bool topEdge = false;
|
|
bool bottomEdge = false;
|
|
bool success = false; // Did we successfully clip this line?
|
|
|
|
// Is the line completely visible?
|
|
point1 = ((*x1 >= clipMinX) && (*x1 <= clipMaxX) && (*y1 >= clipMinY) && (*y1 <= clipMaxY));
|
|
point2 = ((*x2 >= clipMinX) && (*x2 <= clipMaxX) && (*y2 >= clipMinY) && (*y2 <= clipMaxY));
|
|
if (point1 && point2) {
|
|
return(true);
|
|
}
|
|
|
|
// Is the line is completely invisible?
|
|
if (!point1 && !point2) {
|
|
// Test to see if each endpoint is on the same side of one of
|
|
// the bounding planes created by each clipping region boundary
|
|
if (((*x1<clipMinX) && (*x2<clipMinX)) || // left
|
|
((*x1>clipMaxX) && (*x2>clipMaxX)) || // right
|
|
((*y1<clipMinY) && (*y2<clipMinY)) || // above
|
|
((*y1>clipMaxY) && (*y2>clipMaxY))) { // below
|
|
// No need to draw line
|
|
return(false);
|
|
}
|
|
// if we got here we have the special case where the line cuts into and
|
|
// out of the clipping region
|
|
//return(false);
|
|
}
|
|
|
|
// Either endpoint is in clipping region
|
|
if (point1 || (!point1 && !point2)) {
|
|
// Find deltas
|
|
dx = *x2 - *x1;
|
|
dy = *y2 - *y1;
|
|
|
|
// Find which boundary line needs to be clipped against
|
|
if (*x2 > clipMaxX) {
|
|
// Flag right edge
|
|
rightEdge = true;
|
|
// Find intersection with right edge
|
|
if (fabsf(dx) > FLT_EPSILON) { // dx != 0
|
|
yi = (int)((float)0.5 + (dy / dx) * (clipMaxX - *x1) + *y1);
|
|
} else {
|
|
yi = -1; // Invalidate intersection
|
|
}
|
|
} else if (*x2 < clipMinX) {
|
|
// Flag left edge
|
|
leftEdge = true;
|
|
// Find intersection with left edge
|
|
if (fabsf(dx) > FLT_EPSILON) { // dx != 0
|
|
yi = (int)((float)0.5 + (dy / dx) * (clipMinX - *x1) + *y1);
|
|
} else {
|
|
yi = -1; // Invalidate intersection
|
|
}
|
|
}
|
|
|
|
if (*y2 > clipMaxY) {
|
|
// Flag bottom edge
|
|
bottomEdge = true;
|
|
// Find intersection with right edge
|
|
if (fabsf(dy) > FLT_EPSILON) { // dy != 0
|
|
xi = (int)((float)0.5 + (dx / dy) * (clipMaxY - *y1) + *x1);
|
|
} else {
|
|
xi = -1; // Invalidate inntersection
|
|
}
|
|
} else if (*y2 < clipMinY) {
|
|
// Flag top edge
|
|
topEdge = true;
|
|
// Find intersection with top edge
|
|
if (fabsf(dy) > FLT_EPSILON) { // dy != 0
|
|
xi = (int)((float)0.5 + (dx / dy) * (clipMinY - *y1) + *x1);
|
|
} else {
|
|
xi = -1; // Invalidate intersection
|
|
}
|
|
}
|
|
|
|
// We know where the line passed through
|
|
// FInd which edge is the proper intersection
|
|
|
|
if (rightEdge && (yi >= clipMinY && yi <= clipMaxY)) {
|
|
*x2 = clipMaxX;
|
|
*y2 = yi;
|
|
success = true;
|
|
} else if (leftEdge && (yi >= clipMinY && yi <= clipMaxY)) {
|
|
*x2 = clipMinX;
|
|
*y2 = yi;
|
|
success = true;
|
|
}
|
|
|
|
if (bottomEdge && (xi >= clipMinX && xi <= clipMaxX)) {
|
|
*x2 = xi;
|
|
*y2 = clipMaxY;
|
|
success = true;
|
|
} else if (topEdge && (xi >= clipMinX && xi <= clipMaxX)) {
|
|
*x2 = xi;
|
|
*y2 = clipMinY;
|
|
success = true;
|
|
}
|
|
}
|
|
|
|
// Reset edge flags
|
|
rightEdge = false;
|
|
leftEdge = false;
|
|
topEdge = false;
|
|
bottomEdge = false;
|
|
|
|
// Test second endpoint
|
|
|
|
if (point2 || (!point1 && !point2)) {
|
|
// Find deltas
|
|
dx = *x1 - *x2;
|
|
dy = *y1 - *y2;
|
|
|
|
// Find which boundary line needs to be clipped against
|
|
if (*x1 > clipMaxX) {
|
|
// Flag right edge
|
|
rightEdge = true;
|
|
// Find intersection with right edge
|
|
if (fabsf(dx) > FLT_EPSILON) { // dx != 0
|
|
yi = (int)((float)0.5 + (dy / dx) * (clipMaxX - *x2) + *y2);
|
|
} else {
|
|
yi = -1; // Invalidate inntersection
|
|
}
|
|
} else if (*x1 < clipMinX) {
|
|
// Flag left edge
|
|
leftEdge = true;
|
|
// Find intersection with left edge
|
|
if (fabsf(dx) > FLT_EPSILON) { // dx != 0
|
|
yi = (int)((float)0.5 + (dy / dx) * (clipMinX - *x2) + *y2);
|
|
} else {
|
|
yi = -1; // Invalidate intersection
|
|
}
|
|
}
|
|
|
|
if (*y1 > clipMaxY) {
|
|
// Flag bottom edge
|
|
bottomEdge = true;
|
|
// Find intersection with right edge
|
|
if (fabsf(dy) > FLT_EPSILON) { // dy != 0
|
|
xi = (int)((float)0.5 + (dx / dy) * (clipMaxY - *y2) + *x2);
|
|
} else {
|
|
xi = -1; // invalidate inntersection
|
|
}
|
|
} else if (*y1 < clipMinY) {
|
|
// Flag top edge
|
|
topEdge = true;
|
|
// Find intersection with top edge
|
|
if (fabsf(dy) > FLT_EPSILON) { // dy != 0
|
|
xi = (int)((float)0.5 + (dx / dy) * (clipMinY - *y2) + *x2);
|
|
} else {
|
|
xi = -1; // invalidate inntersection
|
|
}
|
|
}
|
|
|
|
// We know where the line passed through
|
|
// Find which edge is the proper intersection
|
|
|
|
if (rightEdge && (yi >= clipMinY && yi <= clipMaxY)) {
|
|
*x1 = clipMaxX;
|
|
*y1 = yi;
|
|
success = true;
|
|
} else if (leftEdge && (yi >= clipMinY && yi <= clipMaxY)) {
|
|
*x1 = clipMinX;
|
|
*y1 = yi;
|
|
success = true;
|
|
}
|
|
|
|
if (bottomEdge && (xi >= clipMinX && xi <= clipMaxX)) {
|
|
*x1 = xi;
|
|
*y1 = clipMaxY;
|
|
success = true;
|
|
} else if (topEdge && (xi >= clipMinX && xi <= clipMaxX)) {
|
|
*x1 = xi;
|
|
*y1 = clipMinY;
|
|
success = true;
|
|
}
|
|
}
|
|
|
|
return(success);
|
|
}
|
|
|
|
|
|
void j3UtilShutdown(void) {
|
|
// Nothing yet
|
|
}
|
|
|
|
|
|
void j3UtilStartup(void) {
|
|
int euler;
|
|
float radian;
|
|
|
|
// Build look-up tables for speed later
|
|
for (euler=0; euler<361; euler++) {
|
|
radian = ((float)M_PI * (float)euler / (float)180);
|
|
sin_table[euler] = (float)sin((double)radian);
|
|
cos_table[euler] = (float)cos((double)radian);
|
|
}
|
|
}
|
|
|
|
|
|
void _j3WorldFree(j3WorldT **world) {
|
|
int x;
|
|
|
|
for (x=0; x<sb_count((*world)->objects); x++) {
|
|
sb_free((*world)->objects[x].triangles);
|
|
sb_free((*world)->objects[x].verticies);
|
|
}
|
|
sb_free((*world)->objects);
|
|
jlFree(*world);
|
|
}
|
|
|
|
|
|
bool _j3WorldLoad(j3WorldT **world, char *file) {
|
|
int r;
|
|
int x;
|
|
char token[1024];
|
|
char *c;
|
|
FILE *in;
|
|
j3CoordinatesT v;
|
|
j3ObjectT o;
|
|
j3TriangleT t;
|
|
|
|
in = fopen(jlUtilMakePathname(file, "obj"), "rt");
|
|
|
|
// Did we find the file?
|
|
if (in == NULL) {
|
|
// Nope.
|
|
return false;
|
|
}
|
|
|
|
// Do we have a world?
|
|
if (*world != NULL) {
|
|
// Free this one
|
|
j3WorldFree(world);
|
|
}
|
|
|
|
// Allocate world object
|
|
*world = (j3WorldT *)jlMalloc(sizeof(j3WorldT));
|
|
|
|
// Initialize world object & create an initial object to read data into
|
|
(*world)->objects = NULL;
|
|
o.vertexCount = 0;
|
|
o.triangleCount = 0;
|
|
o.verticies = NULL;
|
|
o.triangles = NULL;
|
|
|
|
_j3ObjectReset(&o);
|
|
|
|
while (true) {
|
|
|
|
// Read next token
|
|
r = fscanf(in, "%s", token);
|
|
|
|
// End of file?
|
|
if (r == EOF) {
|
|
break;
|
|
}
|
|
|
|
// Vertex?
|
|
if (strcmp(token, "v" ) == 0) {
|
|
r = fscanf(in, "%f %f %f\n", &v.local.x, &v.local.y, &v.local.z);
|
|
sb_push(o.verticies, v);
|
|
o.vertexCount++;
|
|
}
|
|
|
|
// Face?
|
|
if (strcmp(token, "f" ) == 0) {
|
|
for (x=0; x<3; x++) {
|
|
// Fetch 'x'th vertex index
|
|
r = fscanf(in, "%s", token);
|
|
c = strstr(token, "/");
|
|
if (c) c[0] = 0;
|
|
r = atoi(token);
|
|
t.index[x] = r - 1;
|
|
}
|
|
fscanf(in, "\n");
|
|
sb_push(o.triangles, t);
|
|
o.triangleCount++;
|
|
}
|
|
}
|
|
|
|
//***TODO*** support multiple objects - not sure how I want to do this yet
|
|
sb_push((*world)->objects, o);
|
|
|
|
// Finished! Clean up.
|
|
fclose(in);
|
|
|
|
return true;
|
|
}
|
|
|
|
#ifndef JOEY_IIGS
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
|