From 18fab39c08c7c2f7b98f7c682c5083cfbf8975ae Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Thu, 29 Aug 2019 20:18:28 -0500 Subject: [PATCH] Colored and solid objects on IIgs are working. --- j3d/j3d.c | 314 ++++++++++++++++++++++++++++++----------------------- j3d/j3d.h | 22 +++- j3d/main.c | 11 +- 3 files changed, 202 insertions(+), 145 deletions(-) diff --git a/j3d/j3d.c b/j3d/j3d.c index 3256d48..f75b4b7 100644 --- a/j3d/j3d.c +++ b/j3d/j3d.c @@ -25,6 +25,11 @@ // https://archive.org/details/BlackArt3DEBook +//***TODO*** +// Allow multiple objects to be loaded into a world +// Store original vertex data so we can do local rotations and save them + + #include #include #include @@ -465,6 +470,10 @@ void _j3ObjectReset(j3ObjectT *o) { o->scale.x = 1; o->scale.y = 1; o->scale.z = 1; + + o->positionDirty = true; + o->rotationDirty = true; + o->scaleDirty = true; } @@ -491,150 +500,158 @@ void _j3ObjectUpdate(j3ObjectT *o) { // === ROTATION === - x = (jint16)o->rotation.x; - y = (jint16)o->rotation.y; - z = (jint16)o->rotation.z; + if (o->rotationDirty) { - j3MathMatrix4x4Identity(final); + // Rotation being dirty means we also need to update position later + o->positionDirty = true; - // 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; + x = (jint16)o->rotation.x; + y = (jint16)o->rotation.y; + z = (jint16)o->rotation.z; - 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; ivertexCount; 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; + j3MathMatrix4x4Identity(final); - 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; ivertexCount; 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; + // 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; - 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]; + 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; ivertexCount; 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; - final[1][0] = -sin_table[z]; - final[1][1] = cos_table[z]; + 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; ivertexCount; 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; - final[2][0] = sin_table[y] * cos_table[z]; - final[2][1] = sin_table[y] * sin_table[z]; - final[2][2] = cos_table[y]; + 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]; - for (i=0; ivertexCount; 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; + final[1][0] = -sin_table[z]; + final[1][1] = cos_table[z]; - 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; ivertexCount; 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; + final[2][0] = sin_table[y] * cos_table[z]; + final[2][1] = sin_table[y] * sin_table[z]; + final[2][2] = cos_table[y]; - case 5: // Final matrix = x * z - final[0][0] = cos_table[z]; - final[0][1] = sin_table[z]; + for (i=0; ivertexCount; 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; - final[1][0] = -cos_table[x]*sin_table[z]; - final[1][1] = cos_table[x]*cos_table[z]; - final[1][2] = sin_table[x]; + 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; ivertexCount; 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; - final[2][0] = sin_table[x]*sin_table[z]; - final[2][1] = -sin_table[x]*cos_table[z]; - final[2][2] = cos_table[x]; + case 5: // Final matrix = x * z + final[0][0] = cos_table[z]; + final[0][1] = sin_table[z]; - for (i=0; ivertexCount; 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; + final[1][0] = -cos_table[x]*sin_table[z]; + final[1][1] = cos_table[x]*cos_table[z]; + final[1][2] = sin_table[x]; - case 6: // Final matrix = x * y - final[0][0] = cos_table[y]; - final[0][2] = -sin_table[y]; + final[2][0] = sin_table[x]*sin_table[z]; + final[2][1] = -sin_table[x]*cos_table[z]; + final[2][2] = cos_table[x]; - final[1][0] = sin_table[x] * sin_table[y]; - final[1][1] = cos_table[x]; - final[1][2] = sin_table[x] * cos_table[y]; + for (i=0; ivertexCount; 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; - final[2][0] = cos_table[x] * sin_table[y]; - final[2][1] = -sin_table[x]; - final[2][2] = cos_table[x] * cos_table[y]; + case 6: // Final matrix = x * y + final[0][0] = cos_table[y]; + final[0][2] = -sin_table[y]; - for (i=0; ivertexCount; 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; + final[1][0] = sin_table[x] * sin_table[y]; + final[1][1] = cos_table[x]; + final[1][2] = sin_table[x] * cos_table[y]; - 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]; + final[2][0] = cos_table[x] * sin_table[y]; + final[2][1] = -sin_table[x]; + final[2][2] = cos_table[x] * cos_table[y]; - 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]; + for (i=0; ivertexCount; 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; - 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]; + 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]; - j3MathMatrix4x4Mult(rotateX, rotateZ, temp); - j3MathMatrix4x4Mult(temp, rotateZ, final); + 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]; - for (i=0; ivertexCount; 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; + 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]; - default: - break; + j3MathMatrix4x4Mult(rotateX, rotateZ, temp); + j3MathMatrix4x4Mult(temp, rotateZ, final); + + for (i=0; ivertexCount; 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; ivertexCount; 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; + if (o->positionDirty || o->scaleDirty) { + for (i=0; ivertexCount; 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 === @@ -647,6 +664,10 @@ void _j3ObjectUpdate(j3ObjectT *o) { // === REMOVE BACKFACES & LIGHT === for (i=0; itriangleCount; i++) { + // If the scale changed, we need a new normal length + if (o->scaleDirty) { + _j3ObjectUpdateNormalLength(o, (juint16)i); + } // If it's two sided, there are no backfaces if (!o->triangles[i].twoSided) { @@ -740,6 +761,36 @@ void _j3ObjectUpdate(j3ObjectT *o) { } } + + o->positionDirty = false; + o->rotationDirty = false; + o->scaleDirty = false; +} + + +void _j3ObjectUpdateNormalLength(j3ObjectT *object, juint16 triangle) { + juint16 vertex0; + juint16 vertex1; + juint16 vertex2; + j3Vector3DT u; + j3Vector3DT v; + j3Vector3DT normal; + + // Compute length of the two co-planer edges of the polygon, since they will be used in the computation of the dot-product later + vertex0 = object->triangles[triangle].index[0]; + vertex1 = object->triangles[triangle].index[1]; + vertex2 = object->triangles[triangle].index[2]; + + j3MathMakeVector3D((j3VertexT *)&object->verticies[vertex0].local, (j3VertexT *)&object->verticies[vertex1].local, (j3Vector3DT *)&u); + j3MathMakeVector3D((j3VertexT *)&object->verticies[vertex0].local, (j3VertexT *)&object->verticies[vertex2].local, (j3Vector3DT *)&v); + j3MathCrossProduct3D((j3Vector3DT *)&v, (j3Vector3DT *)&u, (j3Vector3DT *)&normal); + + // Compute magnitude of normal, take its inverse and multiply it by + // 15, this will change the shading calculation of 15*dp/normal into + // dp*normal_length, removing one division + object->triangles[triangle].normalLength = (float)15.0 / j3MathVectorMagnatude3D((j3Vector3DT *)&normal); + + } @@ -973,15 +1024,9 @@ void _j3WorldFree(j3WorldT **world) { bool _j3WorldLoad(j3WorldT **world, char *file) { - jint16 x; - jint16 y; - jint16 z; - juint16 vertex0; - juint16 vertex1; - juint16 vertex2; - j3Vector3DT u; - j3Vector3DT v; - j3Vector3DT normal; + juint16 x; + juint16 y; + juint16 z; byte buffer[4]; FILE *in; bool failed = false; @@ -1070,21 +1115,16 @@ bool _j3WorldLoad(j3WorldT **world, char *file) { } if (failed) break; - // Compute length of the two co-planer edges of the polygon, since they will be used in the computation of the dot-product later - vertex0 = (*world)->objects[x].triangles[y].index[0]; - vertex1 = (*world)->objects[x].triangles[y].index[1]; - vertex2 = (*world)->objects[x].triangles[y].index[2]; - j3MathMakeVector3D((j3VertexT *)&(*world)->objects[x].verticies[vertex0].local, (j3VertexT *)&(*world)->objects[x].verticies[vertex1].local, (j3Vector3DT *)&u); - j3MathMakeVector3D((j3VertexT *)&(*world)->objects[x].verticies[vertex0].local, (j3VertexT *)&(*world)->objects[x].verticies[vertex2].local, (j3Vector3DT *)&v); - j3MathCrossProduct3D((j3Vector3DT *)&v, (j3Vector3DT *)&u, (j3Vector3DT *)&normal); + _j3ObjectUpdateNormalLength(&(*world)->objects[x], y); - // Compute magnitude of normal, take its inverse and multiply it by - // 15, this will change the shading calculation of 15*dp/normal into - // dp*normal_length, removing one division - (*world)->objects[x].triangles[y].normalLength = (float)15.0 / j3MathVectorMagnatude3D((j3Vector3DT *)&normal); + // Triangles begin life un-lit + (*world)->objects[x].triangles[y].lit = false; //***TODO*** All triangles are one-sided for now (*world)->objects[x].triangles[y].twoSided = false; + + //***TODO*** All triangles are white for now + (*world)->objects[x].triangles[y].color = 15; } } else { diff --git a/j3d/j3d.h b/j3d/j3d.h index 5f51393..7e5fbb8 100644 --- a/j3d/j3d.h +++ b/j3d/j3d.h @@ -68,6 +68,9 @@ typedef struct { j3VertexT position; // Position of object in world j3VertexT rotation; // Rotation of object in world j3VertexT scale; // Scale of object in world + bool positionDirty; // Did the position change? + bool rotationDirty; // Did the rotation change? + bool scaleDirty; // Did the scale change? } j3ObjectT; typedef struct { @@ -93,32 +96,38 @@ typedef struct { #define j3ObjectMove(ob, px, py, pz) \ ob.position.x += (px); \ ob.position.y += (py); \ - ob.position.z += (pz) + ob.position.z += (pz); \ + ob.positionDirty = true #define j3ObjectMoveTo(ob, px, py, pz) \ ob.position.x = (px); \ ob.position.y = (py); \ - ob.position.z = (pz) + ob.position.z = (pz); \ + ob.positionDirty = true #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) + ob.rotation.z += (pz); j3MathWrapBounds(ob.rotation.z, 0, 360); \ + ob.rotationDirty = true #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) + ob.rotation.z = (pz); j3MathWrapBounds(ob.rotation.z, 0, 360); \ + ob.rotationDirty = true #define j3ObjectScale(ob, px, py, pz) \ ob.scale.x += (px); \ ob.scale.y += (py); \ - ob.scale.z += (pz) + ob.scale.z += (pz); \ + ob.scaleDirty = true #define j3ObjectScaleTo(ob, px, py, pz) \ ob.scale.x = (px); \ ob.scale.y = (py); \ - ob.scale.z = (pz) + ob.scale.z = (pz); \ + ob.scaleDirty = true // Syntatic sugar @@ -149,6 +158,7 @@ void _j3DrawTriangleTop(jint16 x1, jint16 y1, jint16 x2,jint16 y2, jint16 x3, ji void _j3DrawWireframePair(j3ObjectT *o, juint16 v1, juint16 v2); void _j3DrawWireframe(j3ObjectT *o); void _j3ObjectReset(j3ObjectT *o); +void _j3ObjectUpdateNormalLength(j3ObjectT *object, juint16 triangle); void _j3ObjectUpdate(j3ObjectT *o); bool _j3UtilClipLine(jint16 *x1, jint16 *y1, jint16 *x2, jint16 *y2); void _j3WorldFree(j3WorldT **world); diff --git a/j3d/main.c b/j3d/main.c index f5c692d..fcb79e6 100644 --- a/j3d/main.c +++ b/j3d/main.c @@ -87,7 +87,7 @@ int main(void) { c = 1; for (x=0; xobjectCount; x++) { for (y=0; yobjects[x].triangleCount; y++) { - world->objects[x].triangles->color = c++; + world->objects[x].triangles[y].color = c++; if (c > 15) { c = 1; } @@ -137,8 +137,15 @@ int main(void) { j3DrawSolid(world->objects[x]); } + c = 0; + for (x=0; xobjects[0].triangleCount; x++) { + if (world->objects[0].triangles[x].visible) { + c++; + } + } + printAt(font, 1, 1, "Verticies: %d", world->objects[0].vertexCount); - printAt(font, 1, 2, "Triangles: %d", world->objects[0].triangleCount); + printAt(font, 1, 2, "Triangles: %d (%d visible) ", world->objects[0].triangleCount, c); jlDisplayPresent(); }