room101/Apps/E3D.CPP

738 lines
19 KiB
C++

/*******************************************************************************
** File: e3d.cpp
** Author: Derek J. Evans
** Modified: 12 December 1999
**
** Contents: Basic template 3D engine. All code to-be added to Room101.
**
** Copyright (C) 1999-2000 by Derek J. Evans ALL RIGHTS RESERVED
**
** Permission to use, copy, modify, and distribute this software for
** any purpose and without fee is hereby granted, provided that the above
** copyright notice appear in all copies and that both the copyright notice
** and this permission notice appear in supporting documentation.
**
** THIS CODE AND INFORMATION IS PROVIDED "AS-IS" WITHOUT WARRANTY OF ANY KIND,
** EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
** WARRANTIES OF MERCHANTBILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
*******************************************************************************/
#include "../main.hpp"
#include "../file.hpp"
#include "../effects.hpp"
#include "../world.hpp"
#include "../convex.hpp"
#include "../entity.hpp"
#include "../point.hpp"
#include "../particle.hpp"
#include "../vgs.hpp"
#include "../spanner.hpp"
#include "../sound.hpp"
#include "../texture.hpp"
/*******************************************************************************
** Global Variables
*******************************************************************************/
World world;
int bulletid;
int explodeid;
Entity entity_camera;
Entity candles[200];
Entity ent_me;
Entity bullets[16];
Entity explode[16];
Entity ball2;
int texturedepth[50];
Picture* sprites;
Picture* textures;
Picture fb;
Picture zb;
Picture pic_fires[3];
Picture pic_flame;
Picture pic_flame2;
Picture pic_water;
Picture pic_robot;
Picture* font1;
Picture* font2;
Picture glow;
Point skypos;
ParticleEngine partengine;
Camera camera;
Game* game;
Spanner spanner;
Entity** entities;
Sound sound_fire;
Sound sound_explode;
Sound sound_where;
/*******************************************************************************
** EntityAdd
*******************************************************************************/
void
EntityAdd(Entity* entity)
{
entities = (Entity**) MemoryRealloc(entities, NumberOf(entities) + 1, sizeof(Entity*));
entities[NumberOf(entities) - 1] = entity;
}
/*******************************************************************************
** FaceRender
*******************************************************************************/
int nafterfaces;
Face* afterfaces[1000];
Vector render_pos;
World* render_world;
Camera* render_camera;
Matrix render_matrix;
void
FaceRender(Face* face, Spanner* spanner, Matrix matrix)
{
Vertex t1[POLYGON_MAX];
Vertex t2[POLYGON_MAX];
int n = NumberOf(face->vertices);
for (int i = 0; i < n; i++)
{
VectorTransform(t1[i][0], face->vertices[i], matrix);
}
n = VertexArrayClipToFrustum1(t2, t1, n);
if (n >= 3)
{
VertexArrayProject(t2, n, render_camera->origin);
VertexArrayScanXYZ(t2, n);
assert(face->surfaceid > -1);
TextureMap(
&render_world->textures[face->surfaceid],
spanner,
render_camera,
&render_world->surfaces[face->surfaceid],
matrix);
}
}
/*******************************************************************************
** _BSPRender
*******************************************************************************/
void
_BSPRender(BSP* bsp, Matrix matrix)
{
if (bsp && spanner.ngaps && bsp->sides[0])
{
int side = PlaneDistanceTo(bsp->plane, render_pos) > 0.0;
_BSPRender(bsp->sides[side ? 0 : 1], matrix);
for (int i = 0; i < NumberOf(bsp->faces); i++)
{
Face* face = &bsp->faces[i];
if (PlaneDistanceTo(face->plane, render_pos) > 0.0)
{
switch (face->colour)
{
case 0:
break;
case 1:
afterfaces[nafterfaces++] = face;
break;
default:
FaceRender(face, &spanner, matrix);
break;
}
}
}
_BSPRender(bsp->sides[side ? 1 : 0], matrix);
}
}
void
BSPRender(World* world, Camera* camera, Matrix matrix, Vector pos)
{
v3dcpy(render_pos, pos);
render_world = world;
render_camera = camera;
nafterfaces = 0;
_BSPRender(world->bsp, matrix);
}
void
WorldRender(World* world, Camera* camera)
{
Vector zero = {0.0f, 0.0f, 0.0f};
Matrix scale, matrix;
Vector pos;
int i;
MatrixMultiply(matrix, world->matrix, camera->matrix);
VectorInverseTransform(pos, zero, matrix);
MatrixScale(scale, 1.0, camera->aspect, 1.0);
MatrixMultiply(matrix, matrix, scale);
SpannerInvalidate(&spanner);
BSPRender(world, camera, matrix, pos);
for (i = 0; i < NumberOf(entities); i++)
{
EntityRender(entities[i], camera);
}
for (i = 0; i < nafterfaces; i++)
{
FaceRender(afterfaces[i], NULL, matrix);
}
}
void
BehaviourExplode(Entity* entity)
{
entity->issolid = 0;
if (!entity->texture) entity->texture = &pic_fires[explodeid % 3];
entity->scale[0] += 0.15f;
entity->scale[1] += 0.15f;
entity->scale[2] += 0.15f;
if (entity->life-- == 0)
{
entity->behaviour = NULL;
entity->texture = NULL;
}
}
void
EntitySpawn(Entity* dst, Entity* src, void (*behaviour)(Entity*))
{
EntityCreate(dst,
src->pos[0],
src->pos[1],
src->pos[2],
behaviour);
}
void
BehaviourCandle(Entity* entity)
{
entity->texture = &pic_flame;
VectorCreate(entity->scale, 2.0f, 2.0f, 2.0f);
}
void
BehaviourBullet(Entity* entity)
{
entity->radius = 32;
entity->issolid = 0;
entity->texture = &glow;
if (entity->collision)
{
SoundEffect(&sound_explode);
entity->behaviour = NULL;
entity->texture = NULL;
EntitySpawn(&explode[explodeid], entity, BehaviourExplode);
VectorCreate(explode[explodeid].scale, 0.0f, 0.0f, 0.0f);
explode[explodeid].life = 700;
explodeid = (explodeid + 1) % 16;
}
}
void
BehaviourFollow(Entity* entity)
{
Vector vector;
entity->radius = 120;
entity->issolid = 1;
entity->texture = &pic_robot;
entity->vel[1] -= 0.08f;
VectorSubtract(vector, ent_me.pos, entity->pos);
vector[1] = 0.0;
VectorSetLength(vector, vector, 0.1f);
VectorAddition(entity->vel, entity->vel, vector);
if (VectorMagnitude(entity->vel) > 1.0f) VectorSetLength(entity->vel, entity->vel, 1.0f);
}
void
BehaviourCamera(Entity* entity)
{
Matrix xrot, yrot, zrot, matrix, trans;
entity->radius = 32.0f;
entity->issolid = 0;
MatrixTranslate(trans,
-entity->pos[0],
-entity->pos[1],
-entity->pos[2]);
MatrixRotateX(xrot, entity->rot[0]);
MatrixRotateY(yrot, entity->rot[1]);
MatrixRotateZ(zrot, entity->rot[2]);
MatrixMultiply(matrix, trans, yrot, xrot);
CameraLocate(&camera, matrix);
}
void
BehaviourMe(Entity* entity)
{
static Vector force = {0.0f, 0.0f, 1.0f};
static int fire;
static int jump;
static float bounce;
Matrix matrix, xrot, yrot, zrot, xyzrot;
Vector vector;
entity->radius += game->keyboard[KB_C] ? -2 : 2;
entity->radius = Clamp(entity->radius, 92.0f, 128.0f);
entity->issolid = 1;
entity->vel[1] -= 0.1f;
VectorCreate(entity_camera.vel, 0.0f, 0.0f, 0.0f);
v3dcpy(entity_camera.pos, entity->pos);
v3dcpy(entity_camera.rot, entity->rot);
bounce += VectorMagnitude(entity->vel);
float ttt = sin(bounce * DTOR) * (entity->radius / 4.0f) + (entity->radius * 1.4f);
entity_camera.pos[1] += ttt;
MatrixRotateX(xrot, entity->rot[0]);
MatrixRotateY(yrot, entity->rot[1]);
MatrixRotateZ(zrot, entity->rot[2]);
MatrixMultiply(xyzrot, zrot, yrot, xrot);
if (entity->isonground)
{
VectorScale(entity->vel, entity->vel, entity->radius / 134.0f);
MatrixTranspose(matrix, yrot);
VectorRotate(vector, force, matrix);
if (game->keyboard[KB_UPARROW] || game->rightbutton) VectorAddition(entity->vel, entity->vel, vector);
if (game->keyboard[KB_DNARROW] || game->leftbutton ) VectorSubtract(entity->vel, entity->vel, vector);
if (VectorMagnitude(entity->vel) > 4.0f) VectorSetLength(entity->vel, entity->vel, 4.0f);
}
if (game->keyboard[KB_CTRL])
{
if (fire == 0)
{
fire = 1;
SoundEffect(&sound_fire);
VectorCreate(vector, 0.0f, 0.0f, 10.0f);
MatrixTranspose(matrix, xyzrot);
VectorRotate(vector, vector, matrix);
VectorCreate(bullets[bulletid].pos, entity->pos[0], entity->pos[1] + 50, entity->pos[2]);
v3dcpy(bullets[bulletid].vel, vector);
bullets[bulletid].behaviour = BehaviourBullet;
bulletid = (bulletid + 1) % 16;
}
}
else
{
fire = 0;
}
if (game->keyboard[KB_SPACE])
{
if (jump == 0)
{
jump = 1;
entity->vel[1] += 5.0;
}
}
else
{
if (entity->isonground)
{
jump = 0;
}
}
entity->rot[1] += (game->mousevel.x / 4.0f);
entity->rot[0] -= (game->mousevel.y / 4.0f);
if (game->keyboard[KB_LTARROW]) entity->rot[1] -= 1.0f;
if (game->keyboard[KB_RTARROW]) entity->rot[1] += 1.0f;
if (game->keyboard[0x1E]) entity->rot[0] += 1.0f;
if (game->keyboard[0x2C]) entity->rot[0] -= 1.0f;
if (entity->rot[0] > 70.0f) entity->rot[0] = 70.0f;
if (entity->rot[0] < -70.0f) entity->rot[0] = -70.0f;
while (entity->rot[1] > 360.0f) entity->rot[1] -= 360.0f;
while (entity->rot[1] < 0.0f) entity->rot[1] += 360.0f;
skypos.x = (int)(entity->rot[1] * (640.0 / 360.0)) % 640;
skypos.y = 0; //entity->rot[0] * -2.0 + 141.0;
}
float tp = 400;
void
GameMotion(Game* game)
{
Vector vector;
char s[100];
int i, j;
tp -= 0.3f; if (tp < -500) tp = 400;
for (i = 0; i < NumberOf(entities); i++)
{
if (entities[i]->behaviour) entities[i]->behaviour(entities[i]);
}
for (i = 0; i < NumberOf(entities); i++)
{
for (j = i + 1; j < NumberOf(entities); j++)
{
EntityToEntityCollision(entities[i], entities[j]);
}
}
for (i = 0; i < NumberOf(entities); i++)
{
EntityCollisionProcess(entities[i], &world);
}
//MotionTick(&box.rot);
ParticleEngineUpdate(&partengine);
int x, y;
for (y = 0; y < pic_flame.height; y++)
{
x = Round(sin(((y<<3) + (game->timer_count << 2)) * DTOR) * ((pic_flame.height - y) >> 3));
PictureCopy(PICTURE_COPY,
&pic_flame, x, y, pic_flame.width + x, y + 1,
&pic_flame2, 0, y, pic_flame2.width, y + 1,
0, 0, 0);
}
EffectRipple(&textures[1], &pic_water, (360.0f / pic_water.width), 8.0f, game->timer_count);
for (i = 0; i < NumberOf(world.surfaces); i++)
{
Surface* surface = &world.surfaces[i];
if (surface->colour == 2)
{
VectorScale(vector, surface->PMN[1], 0.0005f);
VectorAddition(surface->PMN[0], surface->PMN[0], vector);
}
}
}
void
PictureLineFeed(Picture* picture)
{
picture->y += picture->lineheight;
if (picture->y > (picture->height - picture->lineheight))
{
picture->y -= picture->lineheight;
PictureCopy(PICTURE_COPY,
picture, 0, 0, picture->width, picture->height - picture->lineheight,
picture, 0, picture->lineheight, picture->width, picture->height,
0, 0, 0);
PictureCopy(PICTURE_FILL,
picture, 0, picture->height - picture->lineheight, picture->width, picture->height,
0, 0, 0, 0, 0, 0, 0, 0);
}
}
void
PictureNewLine(Picture* picture)
{
picture->x = 0;
PictureLineFeed(picture);
}
Picture*
FontDownSample(Picture* src)
{
Picture* dst = PictureArrayCreate(NumberOf(src));
for (int i = 0; i < NumberOf(dst); i++)
{
EffectDownSample(&dst[i], &src[i]);
}
return dst;
}
Picture*
FontCharacter(Picture* src, int c)
{
c -= ' ';
if (c < 0 || c >= NumberOf(src)) c = 0;
return &src[c];
}
int
FontTextWidth(Picture* src, char* s)
{
int width = 0;
while (*s) width += FontCharacter(src, *s++)->width;
return width;
}
void
DrawChar(Picture* dst, int c, Picture* src)
{
src = FontCharacter(src, c);
dst->lineheight = Max(src->height, dst->lineheight);
switch (c)
{
case '\n':
{
PictureNewLine(dst);
break;
}
default:
{
PictureCopy(PICTURE_COPY,
dst, dst->x, dst->y, dst->x + src->width, dst->y + src->height,
src, 0, 0, src->width, src->height, 0, 0, 0);
dst->x += src->width;
break;
}
}
}
void
DrawString(Picture* dst, char* s, Picture* src)
{
while (*s) DrawChar(dst, *s++, src);
}
void
PicturePutString(Picture* dst, char* s, Picture* src)
{
int width = FontTextWidth(src, s);
if ((dst->x + width) >= dst->width) PictureNewLine(dst);
DrawString(dst, s, src);
}
void
PictureDrawMemo(Picture* dst, char* s, Picture* src)
{
char* w;
char c;
for (w = s; *s;)
{
c = *s++;
if (isspace(c))
{
c = *s;
*s = '\0';
PicturePutString(dst, w, src);
*s = c;
w = s;
}
}
}
void
GameRender(Game* game)
{
String s;
static int frame;
WorldRender(&world, &camera);
//ParticleEngineRender(&partengine, &camera);
//DrawString(&textures[20], tp, 50,
// "...You have entered Room101. The power of darkness is upon you... ", font2);
//ModelRender(&box, &camera, &fb, &zb);
frame++;
sprintf(s, "%d", game->timer_count / frame);
game->fb.x = 0;
game->fb.y = 0;
//DrawString(&game->fb, s, font2);
}
void
GameCreate(Game* _game)
{
FILE* file;
char s[100];
int i;
game = _game;
printf("Loading fonts\n");
file = FileOpen("../../media/data/MS_SANS_.36", "rb");
font1 = PictureArrayRead(file);
font2 = FontDownSample(font1);
PictureArrayDelete(font1);
FileClose(file);
printf("Creating video window\n");
int h = game->fb.height * 1.0f;
int y = (game->fb.height - h) >> 1;
PictureWindow(&fb, &game->fb, 0, y, game->fb.width, h);
PictureWindow(&zb, &game->zb, 0, y, game->zb.width, h);
PictureLoad(&pic_fires[0], "../../media/data/fire1.hic");
PictureLoad(&pic_fires[1], "../../media/data/fire2.hic");
PictureLoad(&pic_fires[2], "../../media/data/fire3.hic");
PictureLoad(&pic_flame , "../../media/sprites/flame.hic");
PictureLoad(&pic_flame2 , "../../media/sprites/flame.hic");
PictureLoad(&pic_robot , "../../media/sprites/robot.hic");
PictureLoad(&pic_water , "../../media/textures/1.hic");
PictureLoad(&glow , "../../media/data/glow.hic" );
CameraCreate(&camera, &fb, &zb,
((float)fb.width / (float)fb.height) / GamePixelWidth());
SpannerCreate(&spanner, fb.width, fb.height);
printf("Loading textures\n");
textures = (Picture*) MemoryAlloc(21, sizeof(Picture));
for (i = 0; i < NumberOf(textures); i++)
{
sprintf(s, "../../media/textures/%d.hic", i);
PictureLoad(&textures[i], s);
}
file = FileOpen("../../media/textures/depth.txt", "r");
for (i = 0; i < NumberOf(textures); i++)
{
fscanf(file, "%d", &texturedepth[i]);
}
FileClose(file);
printf("Loading sprites\n");
sprites = (Picture*) MemoryAlloc(2, sizeof(Picture));
for (i = 0; i < NumberOf(sprites); i++)
{
sprintf(s, "../../media/sprites/%d.hic", i);
PictureLoad(&sprites[i], s);
}
printf("Reading world\n");
file = FileOpen("../../media/data/bsp.dat", "rb");
WorldRead(&world, textures, texturedepth, file);
FileClose(file);
ParticleEngineCreate(&partengine, 800, 0, 200, 0);
for (i = 0; i < 16; i++)
{
EntityCreate(&bullets[i], 0, 0, 0, NULL);
EntityAdd(&bullets[i]);
EntityCreate(&explode[i], 0, 0, 0, NULL);
EntityAdd(&explode[i]);
}
/*
for (i = 0; i < sphere.XYZ.n; i++)
{
EntityAdd(EntityCreate(
sphere.keyframes.items[0].items[i][0] * 150,
sphere.keyframes.items[0].items[i][1] * 150 + 100,
sphere.keyframes.items[0].items[i][2] * 150,
0, 0, 0, &sprites.items[0], 0, NULL));
}
*/
EntityCreate(&entity_camera, 0, 0, 0, BehaviourCamera);
EntityAdd(&entity_camera);
EntityCreate(&ent_me, 0, 400, 0, BehaviourMe);
EntityAdd(&ent_me);
//EntityCreate(&ball2, 300, 400, 0, BehaviourFollow); EntityAdd(&ball2);
for (i = 0; i < NumberOf(world.lights); i++)
{
Light* light = &world.lights[i];
if (!memcmp(light->name, "candle", 6))
{
EntityCreate(&candles[i], light->pos[0], light->pos[1], light->pos[2], BehaviourCandle);
EntityAdd(&candles[i]);
}
}
/*
EntityCreate(&entity, 0, 300, 0, 0, 0, 0, &sprites[1], 0, BehaviourFollow); EntityAdd(&entity);
EntityCreate(&entity, 0, 300, 0, 0, 0, 0, &sprites[1], 0, BehaviourFollow); EntityAdd(&entity);
EntityCreate(&entity, 0, 300, 0, 0, 0, 0, &sprites[1], 0, BehaviourFollow); EntityAdd(&entity);
EntityCreate(&entity, 0, 300, 0, 0, 0, 0, &sprites[1], 0, BehaviourFollow); EntityAdd(&entity);
*/
printf("Loading sound files\n");
SoundLoadFromFile(&sound_fire , "../../media/sound/mfxlaser.wav");
SoundLoadFromFile(&sound_explode, "../../media/sound/v2tmp0.wav");
//SoundLoadFromFile(&sound_where, "sound/where.wav");
//SoundPlay(&sound_where, 1, 1);
PictureBlitRect(&game->fb, game->fb.rect, PixelCreate(0, 0, 0));
printf("Engine ready\n");
}