Basic wireframe rendering on PC. IIgs not working yet.
This commit is contained in:
parent
38bf774214
commit
06cc056bc5
10 changed files with 1152 additions and 115 deletions
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
font.xcf filter=lfs diff=lfs merge=lfs -text
|
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -1,2 +1,7 @@
|
||||||
*.user
|
|
||||||
*.~
|
*.~
|
||||||
|
*.user
|
||||||
|
*.sta
|
||||||
|
*.dis
|
||||||
|
*.lnk
|
||||||
|
*.map
|
||||||
|
*.sym
|
||||||
|
|
23
build-All.sh
Executable file
23
build-All.sh
Executable file
|
@ -0,0 +1,23 @@
|
||||||
|
#!/bin/bash -e
|
||||||
|
|
||||||
|
PROJECT=j3d
|
||||||
|
GAME=${JOEY}/j3d/j3d
|
||||||
|
DATA=(${GAME}/cube.obj ${GAME}/font.sta)
|
||||||
|
#SOURCE=(*.c *.h)
|
||||||
|
SOURCE=()
|
||||||
|
|
||||||
|
pushd "${GAME}"
|
||||||
|
find . -type f -name "*.xcf" -exec ${JOEY}/utils/xcf2sta.sh {} \;
|
||||||
|
popd
|
||||||
|
|
||||||
|
#. ${JOEY}/dist/IIgs/build-IIgs.helper.sh
|
||||||
|
. ${JOEY}/joeylib/scripts/build-IIgs.helper.sh
|
||||||
|
. ${JOEY}/joeylib/scripts/build-PC.helper.sh
|
||||||
|
|
||||||
|
if [[ "$1x" == "x" ]]; then
|
||||||
|
#buildLinux32
|
||||||
|
buildLinux64
|
||||||
|
#buildWindows32
|
||||||
|
buildWindows64
|
||||||
|
fi
|
||||||
|
buildIIgs $1
|
BIN
font.xcf
(Stored with Git LFS)
Normal file
BIN
font.xcf
(Stored with Git LFS)
Normal file
Binary file not shown.
635
j3d.c
Normal file
635
j3d.c
Normal file
|
@ -0,0 +1,635 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
133
j3d.h
Normal file
133
j3d.h
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef H_J3D_
|
||||||
|
#define H_J3D_
|
||||||
|
|
||||||
|
|
||||||
|
#include "joey.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef float j3Matrix4x4T[4][4];
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float z;
|
||||||
|
} j3VertexT;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
j3VertexT local; // Original vertex positions
|
||||||
|
j3VertexT world; // After rotation, scale, translation (in that order)
|
||||||
|
j3VertexT camera; // Camera space
|
||||||
|
} j3CoordinatesT;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int index[3]; // We do this instead of just a typedef so it works with stretch_buffer
|
||||||
|
} j3TriangleT;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int vertexCount; // How many verticies are in the list
|
||||||
|
int triangleCount; // How many triangles are in the list
|
||||||
|
j3CoordinatesT *verticies; // List of verticies as loaded from disk (more v1, v2, v3 than x, y, z)
|
||||||
|
j3TriangleT *triangles; // Triangles built from vertex list
|
||||||
|
j3VertexT position; // Position of object in world
|
||||||
|
j3VertexT rotation; // Rotation of object in world
|
||||||
|
j3VertexT scale; // Scale of object in world
|
||||||
|
} j3ObjectT;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
j3ObjectT *objects;
|
||||||
|
} j3WorldT;
|
||||||
|
|
||||||
|
|
||||||
|
#define jtCameraSetClippingBounds(x1, y1, x2, y2) \
|
||||||
|
clipMinX = (x1); j3MathCheckBounds(clipMinX, 0, 319); \
|
||||||
|
clipMinY = (y1); j3MathCheckBounds(clipMiny, 0, 199); \
|
||||||
|
clipMaxX = (x2); j3MathCheckBounds(clipMaxX, 0, 319); \
|
||||||
|
clipMaxY = (y2); j3MathCheckBounds(clipMaxy, 0, 199)
|
||||||
|
|
||||||
|
#define j3MathCheckBounds(value, min, max) \
|
||||||
|
if (value < (min)) value = (min); \
|
||||||
|
if (value > (max)) value = (max)
|
||||||
|
|
||||||
|
#define j3MathWrapBounds(value, min, max) \
|
||||||
|
if (value < (min)) value += (max); \
|
||||||
|
if (value > (max)) value -= (max)
|
||||||
|
|
||||||
|
#define j3ObjectMove(ob, px, py, pz) \
|
||||||
|
ob.position.x += (px); \
|
||||||
|
ob.position.y += (py); \
|
||||||
|
ob.position.z += (pz)
|
||||||
|
|
||||||
|
#define j3ObjectMoveTo(ob, px, py, pz) \
|
||||||
|
ob.position.x = (px); \
|
||||||
|
ob.position.y = (py); \
|
||||||
|
ob.position.z = (pz)
|
||||||
|
|
||||||
|
#define j3ObjectRotate(ob, px, py, pz) \
|
||||||
|
ob.rotation.x += (px); j3MathWrapBounds(ob.rotation.x, 0, 360); \
|
||||||
|
ob.rotation.y += (py); j3MathWrapBounds(ob.rotation.y, 0, 360); \
|
||||||
|
ob.rotation.z += (pz); j3MathWrapBounds(ob.rotation.z, 0, 360)
|
||||||
|
|
||||||
|
#define j3ObjectRotateTo(ob, px, py, pz) \
|
||||||
|
ob.rotation.x = (px); j3MathWrapBounds(ob.rotation.x, 0, 360); \
|
||||||
|
ob.rotation.y = (py); j3MathWrapBounds(ob.rotation.y, 0, 360); \
|
||||||
|
ob.rotation.z = (pz); j3MathWrapBounds(ob.rotation.z, 0, 360)
|
||||||
|
|
||||||
|
#define j3ObjectScale(ob, px, py, pz) \
|
||||||
|
ob.scale.x += (px); \
|
||||||
|
ob.scale.y += (py); \
|
||||||
|
ob.scale.z += (pz)
|
||||||
|
|
||||||
|
#define j3ObjectScaleTo(ob, px, py, pz) \
|
||||||
|
ob.scale.x = (px); \
|
||||||
|
ob.scale.y = (py); \
|
||||||
|
ob.scale.z = (pz)
|
||||||
|
|
||||||
|
|
||||||
|
// Syntatic sugar
|
||||||
|
#define j3DrawWireframe(o) _j3DrawWireframe(&(o))
|
||||||
|
#define j3ObjectReset(o) _j3ObjectReset(&(o))
|
||||||
|
#define j3ObjectUpdate(o) _j3ObjectUpdate(&(o))
|
||||||
|
#define j3WorldFree(w) _j3WorldFree((j3WorldT **)&(w))
|
||||||
|
#define j3WorldLoad(w, f) _j3WorldLoad((j3WorldT **)&(w), (f))
|
||||||
|
|
||||||
|
|
||||||
|
// Prototypes
|
||||||
|
void j3MathMatrix4x4Identity(j3Matrix4x4T result);
|
||||||
|
void j3MathMatrix4x4Mult(j3Matrix4x4T a, j3Matrix4x4T b, j3Matrix4x4T result);
|
||||||
|
void j3UtilShutdown(void);
|
||||||
|
void j3UtilStartup(void);
|
||||||
|
|
||||||
|
|
||||||
|
// Private Prototypes
|
||||||
|
void _j3DrawWireframePair(j3ObjectT *o, int v1, int v2);
|
||||||
|
void _j3DrawWireframe(j3ObjectT *o);
|
||||||
|
void _j3ObjectReset(j3ObjectT *o);
|
||||||
|
void _j3ObjectUpdate(j3ObjectT *o);
|
||||||
|
bool _j3UtilClipLine(int *x1, int *y1, int *x2, int *y2);
|
||||||
|
void _j3WorldFree(j3WorldT **world);
|
||||||
|
bool _j3WorldLoad(j3WorldT **world, char *file);
|
||||||
|
|
||||||
|
|
||||||
|
#endif // H_J3D_
|
7
j3d.pro
7
j3d.pro
|
@ -2,12 +2,15 @@ JOEY = /home/scott/joey
|
||||||
include($$JOEY/dist/joey.pri)
|
include($$JOEY/dist/joey.pri)
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
utarray.h
|
stretchy_buffer.h \
|
||||||
|
j3d.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
main.c
|
main.c \
|
||||||
|
j3d.c
|
||||||
|
|
||||||
OTHER_FILES += \
|
OTHER_FILES += \
|
||||||
|
build-All.sh \
|
||||||
postlink.sh \
|
postlink.sh \
|
||||||
notes.txt \
|
notes.txt \
|
||||||
cube.obj
|
cube.obj
|
||||||
|
|
191
main.c
191
main.c
|
@ -1,136 +1,103 @@
|
||||||
#include <stdio.h>
|
/*
|
||||||
#include <math.h>
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "utarray.h"
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#define JOEY_MAIN
|
#define JOEY_MAIN
|
||||||
#include "joey.h"
|
#include "joey.h"
|
||||||
|
|
||||||
#ifdef JOEY_IIGS
|
#ifdef JOEY_IIGS
|
||||||
segment "j3d";
|
segment "j3dTest";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
#include "j3d.h"
|
||||||
//#pragma GCC diagnostic ignored "-Wcast-align"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
float x;
|
|
||||||
float y;
|
|
||||||
float z;
|
|
||||||
} j3VertexT;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
j3VertexT local[3];
|
|
||||||
j3VertexT world[3];
|
|
||||||
j3VertexT camera[3];
|
|
||||||
} j3TriangleT;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
UT_array *verticies;
|
|
||||||
UT_array *triangles;
|
|
||||||
} j3ObjectT;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
UT_array *objects;
|
|
||||||
} j3WorldT;
|
|
||||||
|
|
||||||
|
|
||||||
static UT_icd j3VertexT_icd = { sizeof(j3VertexT), NULL, NULL, NULL };
|
void printAt(jlStaT *font, jint16 x, jint16 y, char *string) {
|
||||||
static UT_icd j3TriangleT_icd = { sizeof(j3TriangleT), NULL, NULL, NULL };
|
juint16 i;
|
||||||
static UT_icd j3WorldT_icd = { sizeof(j3WorldT), NULL, NULL, NULL };
|
byte c;
|
||||||
|
jint16 sx = x;
|
||||||
|
jint16 tx;
|
||||||
|
jint16 ty;
|
||||||
|
|
||||||
|
for (i=0; i<strlen(string); i++) {
|
||||||
bool j3WorldLoad(char *file, j3WorldT *world);
|
c = (byte)string[i];
|
||||||
|
tx = c % 40;
|
||||||
|
ty = c / 40;
|
||||||
bool j3WorldLoad(char *file, j3WorldT *world) {
|
jlDrawBlit8x8(font, tx, ty, sx++, y);
|
||||||
int r;
|
|
||||||
int x;
|
|
||||||
char token[1024];
|
|
||||||
char *c;
|
|
||||||
FILE *in;
|
|
||||||
j3VertexT *vp;
|
|
||||||
j3VertexT v;
|
|
||||||
j3ObjectT o;
|
|
||||||
j3TriangleT t;
|
|
||||||
|
|
||||||
in = fopen(jlUtilMakePathname(file, "obj"), "rt");
|
|
||||||
|
|
||||||
// Did we find the file?
|
|
||||||
if (in == NULL) {
|
|
||||||
// Nope.
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize object array inside world object
|
|
||||||
utarray_new(world->objects, &j3WorldT_icd);
|
|
||||||
|
|
||||||
// Create an initial object to read data into (***TODO*** support multiple objects)
|
|
||||||
utarray_new(o.verticies, &j3VertexT_icd);
|
|
||||||
utarray_new(o.triangles, &j3TriangleT_icd);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
|
|
||||||
// Read next token
|
|
||||||
r = fscanf(in, "%s", token);
|
|
||||||
//printf("fscanf s = %d [%s]\n", r, token);
|
|
||||||
|
|
||||||
// End of file?
|
|
||||||
if (r == EOF) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vertex?
|
|
||||||
if (strcmp(token, "v" ) == 0) {
|
|
||||||
r = fscanf(in, "%f %f %f\n", &v.x, &v.y, &v.z);
|
|
||||||
//printf("fscanf f f f = %d [%s]\n", r, token);
|
|
||||||
//printf("Vertex: %f %f %f\n", v.x, v.y, v.z);
|
|
||||||
utarray_push_back(o.verticies, &v);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Face?
|
|
||||||
if (strcmp(token, "f" ) == 0) {
|
|
||||||
//printf("Triangle:\n");
|
|
||||||
for (x=0; x<3; x++) {
|
|
||||||
// Fetch 'x'th vertex index
|
|
||||||
r = fscanf(in, "%s", token);
|
|
||||||
//printf("fscanf s = %d [%s]\n", r, token);
|
|
||||||
c = strstr(token, "/");
|
|
||||||
if (c) c[0] = 0;
|
|
||||||
r = atoi(token);
|
|
||||||
//printf("atoi = %d\n", r);
|
|
||||||
vp = (j3VertexT *)utarray_eltptr(o.verticies, (unsigned int)r - 1);
|
|
||||||
t.local[x] = *vp;
|
|
||||||
//printf(" Face Index: %s Vertex: %f %f %f\n", token, vp->x, vp->y, vp->z);
|
|
||||||
}
|
|
||||||
fscanf(in, "\n");
|
|
||||||
utarray_push_back(o.triangles, &t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finished! Clean up.
|
|
||||||
fclose(in);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
|
jlStaT *font = NULL;
|
||||||
j3WorldT world;
|
j3WorldT *world = NULL;
|
||||||
j3ObjectT *object;
|
bool r;
|
||||||
|
|
||||||
jlUtilStartup("JoeyLib 3D");
|
jlUtilStartup("JoeyLib 3D");
|
||||||
|
jlStaLoad(font, "font");
|
||||||
|
|
||||||
j3WorldLoad("cube", &world);
|
printAt(font, 1, 1, "Starting JoeyLib3D...");
|
||||||
|
jlDisplayPresent();
|
||||||
|
j3UtilStartup();
|
||||||
|
|
||||||
printf("Made it here\n");
|
printAt(font, 1, 1, "Loading object... ");
|
||||||
|
jlDisplayPresent();
|
||||||
|
r = j3WorldLoad(world, "cube");
|
||||||
|
if (r) {
|
||||||
|
printAt(font, 1, 1, "Object loading: Success!");
|
||||||
|
jlDisplayPresent();
|
||||||
|
} else {
|
||||||
|
printAt(font, 1, 1, "Object loading: Failed.");
|
||||||
|
jlDisplayPresent();
|
||||||
|
}
|
||||||
|
|
||||||
object = (j3ObjectT *)utarray_front(world.objects);
|
//***TODO*** Add methods to inspect world/object information
|
||||||
printf("Verticies: %d\n", utarray_len(object->verticies));
|
//printf("Verticies: %d\n", sb_count(world->objects[0].verticies));
|
||||||
printf("Triangles: %d\n", utarray_len(object->triangles));
|
//printf("Triangles: %d\n", sb_count(world->objects[0].triangles));
|
||||||
|
|
||||||
jlKeyWaitForAny();
|
j3ObjectMoveTo(world->objects[0], 0, 0, 300); // Matching values in code I'm studying
|
||||||
|
j3ObjectScaleTo(world->objects[0], 30, 30, 30); // Matching values in code I'm studying
|
||||||
|
|
||||||
|
while (!jlKeyPressed() && !jlUtilMustExit()) {
|
||||||
|
j3ObjectRotate(world->objects[0], 2, 4, 6); // Matching values in code I'm studying
|
||||||
|
j3ObjectUpdate(world->objects[0]);
|
||||||
|
|
||||||
|
jlDrawColor(0);
|
||||||
|
jlDrawClear();
|
||||||
|
|
||||||
|
jlDrawColor(15);
|
||||||
|
jlDrawBox(0, 0, 319, 199);
|
||||||
|
j3DrawWireframe(world->objects[0]);
|
||||||
|
|
||||||
|
jlDisplayPresent();
|
||||||
|
}
|
||||||
|
jlKeyRead();
|
||||||
|
|
||||||
|
j3WorldFree(world);
|
||||||
|
jlStaFree(font);
|
||||||
|
|
||||||
|
j3UtilShutdown();
|
||||||
jlUtilShutdown();
|
jlUtilShutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
|
|
|
@ -3,5 +3,10 @@
|
||||||
GAME=$1
|
GAME=$1
|
||||||
export JOEY=$2
|
export JOEY=$2
|
||||||
|
|
||||||
|
pushd "${GAME}"
|
||||||
|
find . -type f -name "*.xcf" -exec ${JOEY}/utils/xcf2sta.sh {} \;
|
||||||
|
popd
|
||||||
|
|
||||||
mkdir -p data
|
mkdir -p data
|
||||||
|
cp -f "${GAME}"/*.sta data/.
|
||||||
cp -f "${GAME}"/*.obj data/.
|
cp -f "${GAME}"/*.obj data/.
|
||||||
|
|
262
stretchy_buffer.h
Normal file
262
stretchy_buffer.h
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
// stretchy_buffer.h - v1.03 - public domain - nothings.org/stb
|
||||||
|
// a vector<>-like dynamic array for C
|
||||||
|
//
|
||||||
|
// version history:
|
||||||
|
// 1.03 - compile as C++ maybe
|
||||||
|
// 1.02 - tweaks to syntax for no good reason
|
||||||
|
// 1.01 - added a "common uses" documentation section
|
||||||
|
// 1.0 - fixed bug in the version I posted prematurely
|
||||||
|
// 0.9 - rewrite to try to avoid strict-aliasing optimization
|
||||||
|
// issues, but won't compile as C++
|
||||||
|
//
|
||||||
|
// Will probably not work correctly with strict-aliasing optimizations.
|
||||||
|
//
|
||||||
|
// The idea:
|
||||||
|
//
|
||||||
|
// This implements an approximation to C++ vector<> for C, in that it
|
||||||
|
// provides a generic definition for dynamic arrays which you can
|
||||||
|
// still access in a typesafe way using arr[i] or *(arr+i). However,
|
||||||
|
// it is simply a convenience wrapper around the common idiom of
|
||||||
|
// of keeping a set of variables (in a struct or globals) which store
|
||||||
|
// - pointer to array
|
||||||
|
// - the length of the "in-use" part of the array
|
||||||
|
// - the current size of the allocated array
|
||||||
|
//
|
||||||
|
// I find it to be the single most useful non-built-in-structure when
|
||||||
|
// programming in C (hash tables a close second), but to be clear
|
||||||
|
// it lacks many of the capabilities of C++ vector<>: there is no
|
||||||
|
// range checking, the object address isn't stable (see next section
|
||||||
|
// for details), the set of methods available is small (although
|
||||||
|
// the file stb.h has another implementation of stretchy buffers
|
||||||
|
// called 'stb_arr' which provides more methods, e.g. for insertion
|
||||||
|
// and deletion).
|
||||||
|
//
|
||||||
|
// How to use:
|
||||||
|
//
|
||||||
|
// Unlike other stb header file libraries, there is no need to
|
||||||
|
// define an _IMPLEMENTATION symbol. Every #include creates as
|
||||||
|
// much implementation is needed.
|
||||||
|
//
|
||||||
|
// stretchy_buffer.h does not define any types, so you do not
|
||||||
|
// need to #include it to before defining data types that are
|
||||||
|
// stretchy buffers, only in files that *manipulate* stretchy
|
||||||
|
// buffers.
|
||||||
|
//
|
||||||
|
// If you want a stretchy buffer aka dynamic array containing
|
||||||
|
// objects of TYPE, declare such an array as:
|
||||||
|
//
|
||||||
|
// TYPE *myarray = NULL;
|
||||||
|
//
|
||||||
|
// (There is no typesafe way to distinguish between stretchy
|
||||||
|
// buffers and regular arrays/pointers; this is necessary to
|
||||||
|
// make ordinary array indexing work on these objects.)
|
||||||
|
//
|
||||||
|
// Unlike C++ vector<>, the stretchy_buffer has the same
|
||||||
|
// semantics as an object that you manually malloc and realloc.
|
||||||
|
// The pointer may relocate every time you add a new object
|
||||||
|
// to it, so you:
|
||||||
|
//
|
||||||
|
// 1. can't take long-term pointers to elements of the array
|
||||||
|
// 2. have to return the pointer from functions which might expand it
|
||||||
|
// (either as a return value or by storing it to a ptr-to-ptr)
|
||||||
|
//
|
||||||
|
// Now you can do the following things with this array:
|
||||||
|
//
|
||||||
|
// sb_free(TYPE *a) free the array
|
||||||
|
// sb_count(TYPE *a) the number of elements in the array
|
||||||
|
// sb_push(TYPE *a, TYPE v) adds v on the end of the array, a la push_back
|
||||||
|
// sb_add(TYPE *a, int n) adds n uninitialized elements at end of array & returns pointer to first added
|
||||||
|
// sb_last(TYPE *a) returns an lvalue of the last item in the array
|
||||||
|
// a[n] access the nth (counting from 0) element of the array
|
||||||
|
//
|
||||||
|
// #define STRETCHY_BUFFER_NO_SHORT_NAMES to only export
|
||||||
|
// names of the form 'stb_sb_' if you have a name that would
|
||||||
|
// otherwise collide.
|
||||||
|
//
|
||||||
|
// Note that these are all macros and many of them evaluate
|
||||||
|
// their arguments more than once, so the arguments should
|
||||||
|
// be side-effect-free.
|
||||||
|
//
|
||||||
|
// Note that 'TYPE *a' in sb_push and sb_add must be lvalues
|
||||||
|
// so that the library can overwrite the existing pointer if
|
||||||
|
// the object has to be reallocated.
|
||||||
|
//
|
||||||
|
// In an out-of-memory condition, the code will try to
|
||||||
|
// set up a null-pointer or otherwise-invalid-pointer
|
||||||
|
// exception to happen later. It's possible optimizing
|
||||||
|
// compilers could detect this write-to-null statically
|
||||||
|
// and optimize away some of the code, but it should only
|
||||||
|
// be along the failure path. Nevertheless, for more security
|
||||||
|
// in the face of such compilers, #define STRETCHY_BUFFER_OUT_OF_MEMORY
|
||||||
|
// to a statement such as assert(0) or exit(1) or something
|
||||||
|
// to force a failure when out-of-memory occurs.
|
||||||
|
//
|
||||||
|
// Common use:
|
||||||
|
//
|
||||||
|
// The main application for this is when building a list of
|
||||||
|
// things with an unknown quantity, either due to loading from
|
||||||
|
// a file or through a process which produces an unpredictable
|
||||||
|
// number.
|
||||||
|
//
|
||||||
|
// My most common idiom is something like:
|
||||||
|
//
|
||||||
|
// SomeStruct *arr = NULL;
|
||||||
|
// while (something)
|
||||||
|
// {
|
||||||
|
// SomeStruct new_one;
|
||||||
|
// new_one.whatever = whatever;
|
||||||
|
// new_one.whatup = whatup;
|
||||||
|
// new_one.foobar = barfoo;
|
||||||
|
// sb_push(arr, new_one);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// and various closely-related factorings of that. For example,
|
||||||
|
// you might have several functions to create/init new SomeStructs,
|
||||||
|
// and if you use the above idiom, you might prefer to make them
|
||||||
|
// return structs rather than take non-const-pointers-to-structs,
|
||||||
|
// so you can do things like:
|
||||||
|
//
|
||||||
|
// SomeStruct *arr = NULL;
|
||||||
|
// while (something)
|
||||||
|
// {
|
||||||
|
// if (case_A) {
|
||||||
|
// sb_push(arr, some_func1());
|
||||||
|
// } else if (case_B) {
|
||||||
|
// sb_push(arr, some_func2());
|
||||||
|
// } else {
|
||||||
|
// sb_push(arr, some_func3());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Note that the above relies on the fact that sb_push doesn't
|
||||||
|
// evaluate its second argument more than once. The macros do
|
||||||
|
// evaluate the *array* argument multiple times, and numeric
|
||||||
|
// arguments may be evaluated multiple times, but you can rely
|
||||||
|
// on the second argument of sb_push being evaluated only once.
|
||||||
|
//
|
||||||
|
// Of course, you don't have to store bare objects in the array;
|
||||||
|
// if you need the objects to have stable pointers, store an array
|
||||||
|
// of pointers instead:
|
||||||
|
//
|
||||||
|
// SomeStruct **arr = NULL;
|
||||||
|
// while (something)
|
||||||
|
// {
|
||||||
|
// SomeStruct *new_one = malloc(sizeof(*new_one));
|
||||||
|
// new_one->whatever = whatever;
|
||||||
|
// new_one->whatup = whatup;
|
||||||
|
// new_one->foobar = barfoo;
|
||||||
|
// sb_push(arr, new_one);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// How it works:
|
||||||
|
//
|
||||||
|
// A long-standing tradition in things like malloc implementations
|
||||||
|
// is to store extra data before the beginning of the block returned
|
||||||
|
// to the user. The stretchy buffer implementation here uses the
|
||||||
|
// same trick; the current-count and current-allocation-size are
|
||||||
|
// stored before the beginning of the array returned to the user.
|
||||||
|
// (This means you can't directly free() the pointer, because the
|
||||||
|
// allocated pointer is different from the type-safe pointer provided
|
||||||
|
// to the user.)
|
||||||
|
//
|
||||||
|
// The details are trivial and implementation is straightforward;
|
||||||
|
// the main trick is in realizing in the first place that it's
|
||||||
|
// possible to do this in a generic, type-safe way in C.
|
||||||
|
//
|
||||||
|
// Contributors:
|
||||||
|
//
|
||||||
|
// Timothy Wright (github:ZenToad)
|
||||||
|
//
|
||||||
|
// LICENSE
|
||||||
|
//
|
||||||
|
// See end of file for license information.
|
||||||
|
|
||||||
|
#ifndef STB_STRETCHY_BUFFER_H_INCLUDED
|
||||||
|
#define STB_STRETCHY_BUFFER_H_INCLUDED
|
||||||
|
|
||||||
|
#ifndef NO_STRETCHY_BUFFER_SHORT_NAMES
|
||||||
|
#define sb_free stb_sb_free
|
||||||
|
#define sb_push stb_sb_push
|
||||||
|
#define sb_count stb_sb_count
|
||||||
|
#define sb_add stb_sb_add
|
||||||
|
#define sb_last stb_sb_last
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define stb_sb_free(a) ((a) ? free(stb__sbraw(a)),0 : 0)
|
||||||
|
#define stb_sb_push(a,v) (stb__sbmaybegrow(a,1), (a)[stb__sbn(a)++] = (v))
|
||||||
|
#define stb_sb_count(a) ((a) ? stb__sbn(a) : 0)
|
||||||
|
#define stb_sb_add(a,n) (stb__sbmaybegrow(a,n), stb__sbn(a)+=(n), &(a)[stb__sbn(a)-(n)])
|
||||||
|
#define stb_sb_last(a) ((a)[stb__sbn(a)-1])
|
||||||
|
|
||||||
|
#define stb__sbraw(a) ((int *) (a) - 2)
|
||||||
|
#define stb__sbm(a) stb__sbraw(a)[0]
|
||||||
|
#define stb__sbn(a) stb__sbraw(a)[1]
|
||||||
|
|
||||||
|
#define stb__sbneedgrow(a,n) ((a)==0 || stb__sbn(a)+(n) >= stb__sbm(a))
|
||||||
|
#define stb__sbmaybegrow(a,n) (stb__sbneedgrow(a,(n)) ? stb__sbgrow(a,n) : 0)
|
||||||
|
#define stb__sbgrow(a,n) (*((void **)&(a)) = stb__sbgrowf((a), (n), sizeof(*(a))))
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
static void * stb__sbgrowf(void *arr, int increment, int itemsize)
|
||||||
|
{
|
||||||
|
int dbl_cur = arr ? 2*stb__sbm(arr) : 0;
|
||||||
|
int min_needed = stb_sb_count(arr) + increment;
|
||||||
|
int m = dbl_cur > min_needed ? dbl_cur : min_needed;
|
||||||
|
int *p = (int *) realloc(arr ? stb__sbraw(arr) : 0, itemsize * m + sizeof(int)*2);
|
||||||
|
if (p) {
|
||||||
|
if (!arr)
|
||||||
|
p[1] = 0;
|
||||||
|
p[0] = m;
|
||||||
|
return p+2;
|
||||||
|
} else {
|
||||||
|
#ifdef STRETCHY_BUFFER_OUT_OF_MEMORY
|
||||||
|
STRETCHY_BUFFER_OUT_OF_MEMORY ;
|
||||||
|
#endif
|
||||||
|
return (void *) (2*sizeof(int)); // try to force a NULL pointer exception later
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // STB_STRETCHY_BUFFER_H_INCLUDED
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
This software is available under 2 licenses -- choose whichever you prefer.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE A - MIT License
|
||||||
|
Copyright (c) 2017 Sean Barrett
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||||
|
software, either in source code form or as a compiled binary, for any purpose,
|
||||||
|
commercial or non-commercial, and by any means.
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||||
|
software dedicate any and all copyright interest in the software to the public
|
||||||
|
domain. We make this dedication for the benefit of the public at large and to
|
||||||
|
the detriment of our heirs and successors. We intend this dedication to be an
|
||||||
|
overt act of relinquishment in perpetuity of all present and future rights to
|
||||||
|
this software under copyright law.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
Loading…
Add table
Reference in a new issue