From b0671fcac8564a24ec6d499c0a574d71d937ea7d Mon Sep 17 00:00:00 2001 From: havoc Date: Thu, 25 Jul 2002 09:14:48 +0000 Subject: [PATCH] .dpv video playback is now implemented. Slow though, because it's resizing textures, this needs to be fixed... git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@2083 d7cf8633-e32d-0410-b094-e92efae38249 --- cl_main.c | 3 + cl_screen.c | 58 ++-- cl_screen.h | 22 +- cl_video.c | 243 ++++++++++++++++ cl_video.h | 12 + darkplaces.dsp | 32 ++- dpvsimpledecode.c | 708 ++++++++++++++++++++++++++++++++++++++++++++++ dpvsimpledecode.h | 49 ++++ draw.h | 4 + gl_draw.c | 135 +++++++++ gl_textures.c | 11 +- host.c | 5 +- makefile | 2 +- makefile.mingw | 2 +- menu.c | 35 ++- r_textures.h | 3 + snd_dma.c | 39 ++- sound.h | 5 + wavefile.c | 193 +++++++++++++ wavefile.h | 61 ++++ 20 files changed, 1575 insertions(+), 47 deletions(-) create mode 100644 cl_video.c create mode 100644 cl_video.h create mode 100644 dpvsimpledecode.c create mode 100644 dpvsimpledecode.h create mode 100644 wavefile.c create mode 100644 wavefile.h diff --git a/cl_main.c b/cl_main.c index 4f83e460..f64a4e05 100644 --- a/cl_main.c +++ b/cl_main.c @@ -21,6 +21,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "quakedef.h" #include "cl_collision.h" +#include "cl_video.h" // we need to declare some mouse variables here, because the menu system // references them even when on a unix system. @@ -975,5 +976,7 @@ void CL_Init (void) CL_Particles_Init(); CL_Screen_Init(); CL_CGVM_Init(); + + CL_Video_Init(); } diff --git a/cl_screen.c b/cl_screen.c index 36477292..9feb591a 100644 --- a/cl_screen.c +++ b/cl_screen.c @@ -1,5 +1,6 @@ #include "quakedef.h" +#include "cl_video.h" cvar_t scr_viewsize = {CVAR_SAVE, "viewsize","100"}; cvar_t scr_fov = {CVAR_SAVE, "fov","90"}; // 10 - 170 @@ -565,34 +566,41 @@ void DrawQ_Fill (float x, float y, float w, float h, float red, float green, flo r_refdef.drawqueuesize += dq->size; } -//only used for the player color selection menu -void DrawQ_PicTranslate (int x, int y, char *picname, qbyte *translation) +void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags) { - int i, c; - unsigned int trans[4096]; - cachepic_t *pic; - - pic = Draw_CachePic(picname); - if (pic == NULL) - return; - - c = pic->width * pic->height; - if (c > 4096) - { - Con_Printf("DrawQ_PicTranslate: image larger than 4k buffer\n"); + int size; + void *p; + drawqueue_t *dq; + drawqueuemesh_t *m; + size = sizeof(*dq); + size += sizeof(drawqueuemesh_t); + size += sizeof(int) * mesh->numindices; + size += sizeof(float[3]) * mesh->numvertices; + size += sizeof(float[2]) * mesh->numvertices; + size += sizeof(float[4]) * mesh->numvertices; + if (r_refdef.drawqueuesize + size > MAX_DRAWQUEUE) return; - } - - for (i = 0;i < c;i++) - trans[i] = d_8to24table[translation[menuplyr_pixels[i]]]; - - // FIXME: this is renderer stuff? - R_UpdateTexture (pic->tex, (qbyte *)trans); - - DrawQ_Pic(x, y, picname, 0, 0, 1, 1, 1, 1, 0); + dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize); + dq->size = size; + dq->command = DRAWQUEUE_MESH; + dq->flags = flags; + dq->color = 0; + dq->x = 0; + dq->y = 0; + dq->scalex = 0; + dq->scaley = 0; + p = (void *)(dq + 1); + m = p;p += sizeof(drawqueuemesh_t); + m->numindices = mesh->numindices; + m->numvertices = mesh->numvertices; + m->texture = mesh->texture; + m->indices = p;memcpy(m->indices , mesh->indices , m->numindices * sizeof(int ));p += m->numindices * sizeof(int ); + m->vertices = p;memcpy(m->vertices , mesh->vertices , m->numvertices * sizeof(float[3]));p += m->numvertices * sizeof(float[3]); + m->texcoords = p;memcpy(m->texcoords, mesh->texcoords, m->numvertices * sizeof(float[2]));p += m->numvertices * sizeof(float[2]); + m->colors = p;memcpy(m->colors , mesh->colors , m->numvertices * sizeof(float[4]));p += m->numvertices * sizeof(float[4]); + r_refdef.drawqueuesize += dq->size; } - /* ==================== CalcFov @@ -960,6 +968,8 @@ void CL_UpdateScreen(void) SCR_DrawLoading(); } + CL_DrawVideo(); + R_TimeReport("2d"); // add r_speeds text to queue diff --git a/cl_screen.h b/cl_screen.h index 42575f03..57552be4 100644 --- a/cl_screen.h +++ b/cl_screen.h @@ -7,6 +7,7 @@ #define DRAWQUEUE_PIC 0 #define DRAWQUEUE_STRING 1 +#define DRAWQUEUE_MESH 2 typedef struct drawqueue_s { @@ -17,14 +18,31 @@ typedef struct drawqueue_s } drawqueue_t; +// a triangle mesh... embedded in the drawqueue +typedef struct drawqueuemesh_s +{ + rtexture_t *texture; + int numindices; + int numvertices; + int *indices; + float *vertices; + float *texcoords; + qbyte *colors; +} +drawqueuemesh_t; + #define DRAWFLAG_ADDITIVE 1 +// clear the draw queue void DrawQ_Clear(void); +// draw an image void DrawQ_Pic(float x, float y, char *picname, float width, float height, float red, float green, float blue, float alpha, int flags); +// draw a text string void DrawQ_String(float x, float y, char *string, int maxlen, float scalex, float scaley, float red, float green, float blue, float alpha, int flags); +// draw a filled rectangle void DrawQ_Fill (float x, float y, float w, float h, float red, float green, float blue, float alpha, int flags); -// only used for player config menu -void DrawQ_PicTranslate (int x, int y, char *picname, qbyte *translation); +// draw a triangle mesh +void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags); void SHOWLMP_decodehide(void); void SHOWLMP_decodeshow(void); diff --git a/cl_video.c b/cl_video.c new file mode 100644 index 00000000..fd4c7071 --- /dev/null +++ b/cl_video.c @@ -0,0 +1,243 @@ + +#include "quakedef.h" +#include "cl_video.h" +#include "dpvsimpledecode.h" + +mempool_t *clvideomempool; + +int cl_videoplaying = false; +void *cl_videostream; + +double cl_videostarttime; +int cl_videoframenum; +double cl_videoframerate; + +int cl_videoimagewidth; +int cl_videoimageheight; +int cl_videoimagedata_rmask; +int cl_videoimagedata_gmask; +int cl_videoimagedata_bmask; +int cl_videoimagedata_bytesperpixel; +void *cl_videoimagedata; + +int cl_videosoundrate; +int cl_videosoundlength; +short *cl_videosounddata; +int cl_videosoundresamplelength; +short *cl_videosoundresampledata; + +rtexture_t *cl_videotexture; +rtexturepool_t *cl_videotexturepool; + +void CL_VideoFrame(void) +{ + int frames, framenum, samples, s; + if (!cl_videoplaying) + return; + framenum = (realtime - cl_videostarttime) * cl_videoframerate; + //Con_Printf("frame %i\n", framenum); + if (framenum < 0) + framenum = 0; + frames = 0; + while (cl_videoframenum < framenum) + { + frames++; + cl_videoframenum++; + if (dpvsimpledecode_video(cl_videostream, cl_videoimagedata, cl_videoimagedata_rmask, cl_videoimagedata_gmask, cl_videoimagedata_bmask, cl_videoimagedata_bytesperpixel, cl_videoimagewidth * cl_videoimagedata_bytesperpixel)) + { + CL_VideoStop(); + return; + } + } + if (frames) + { + R_UpdateTexture(cl_videotexture, cl_videoimagedata); + //Draw_NewPic("engine_videoframe", cl_videoimagewidth, cl_videoimageheight, false, cl_videoimagedata); + } + if (cl_videosoundrate && (samples = S_RawSamples_QueueWantsMore())) + { + Con_Printf("%i = S_RawSamples_QueueWantsMore()\n", samples); + + // calculate how much source data we need to fill the output... + s = samples * cl_videosoundrate / shm->speed; + + // reallocate processing buffer if needed + if (cl_videosoundresamplelength < samples) + { + cl_videosoundresamplelength = samples + 100; + if (cl_videosoundresampledata) + Mem_Free(cl_videosoundresampledata); + cl_videosoundresampledata = Mem_Alloc(clvideomempool, cl_videosoundresamplelength * sizeof(short[2])); + } + + // reallocate loading buffer if needed + if (cl_videosoundlength < s) + { + cl_videosoundlength = s + 100; + if (cl_videosounddata) + Mem_Free(cl_videosounddata); + cl_videosounddata = Mem_Alloc(clvideomempool, cl_videosoundlength * sizeof(short[2])); + } + + dpvsimpledecode_audio(cl_videostream, cl_videosounddata, s); + S_ResampleBuffer16Stereo(cl_videosounddata, s, cl_videosoundresampledata, samples); + S_RawSamples_Enqueue(cl_videosoundresampledata, samples); + } +} + +void CL_DrawVideo(void) +{ + if (cl_videoplaying) + { + drawqueuemesh_t mesh; + int indices[6]; + float vertices[12]; + float texcoords[8]; + qbyte colorsb[16]; + float s1, t1, s2, t2, x1, y1, x2, y2; + indices[0] = 0; + indices[1] = 1; + indices[2] = 2; + indices[3] = 0; + indices[4] = 2; + indices[5] = 3; + x1 = 0; + y1 = 0; + x2 = vid.conwidth; + y2 = vid.conheight; + vertices[0] = y1; + vertices[1] = y1; + vertices[2] = 0; + vertices[3] = x2; + vertices[4] = y1; + vertices[5] = 0; + vertices[6] = x2; + vertices[7] = y2; + vertices[8] = 0; + vertices[9] = x1; + vertices[10] = y2; + vertices[11] = 0; + R_FragmentLocation(cl_videotexture, NULL, NULL, &s1, &t1, &s2, &t2); + texcoords[0] = s1; + texcoords[1] = t1; + texcoords[2] = s2; + texcoords[3] = t1; + texcoords[4] = s2; + texcoords[5] = t2; + texcoords[6] = s1; + texcoords[7] = t2; + colorsb[0] = 255 >> v_overbrightbits.integer; + colorsb[1] = 255 >> v_overbrightbits.integer; + colorsb[2] = 255 >> v_overbrightbits.integer; + colorsb[3] = 255; + colorsb[4] = 255 >> v_overbrightbits.integer; + colorsb[5] = 255 >> v_overbrightbits.integer; + colorsb[6] = 255 >> v_overbrightbits.integer; + colorsb[7] = 255; + colorsb[8] = 255 >> v_overbrightbits.integer; + colorsb[9] = 255 >> v_overbrightbits.integer; + colorsb[10] = 255 >> v_overbrightbits.integer; + colorsb[11] = 255; + colorsb[12] = 255 >> v_overbrightbits.integer; + colorsb[13] = 255 >> v_overbrightbits.integer; + colorsb[14] = 255 >> v_overbrightbits.integer; + colorsb[15] = 255; + mesh.texture = cl_videotexture; + mesh.numindices = 6; + mesh.numvertices = 4; + mesh.indices = indices; + mesh.vertices = vertices; + mesh.texcoords = texcoords; + mesh.colors = colorsb; + DrawQ_Mesh(&mesh, 0); + //DrawQ_Pic(0, 0, "engine_videoframe", vid.conwidth, vid.conheight, 1, 1, 1, 1, 0); + } +} + +void CL_VideoStart(char *filename) +{ + char *errorstring; + cl_videostream = dpvsimpledecode_open(filename, &errorstring); + if (!cl_videostream) + { + Con_Printf("unable to open \"%s\", error: %s\n", filename, errorstring); + return; + } + + cl_videoplaying = true; + cl_videostarttime = realtime; + cl_videoframenum = -1; + cl_videoframerate = dpvsimpledecode_getframerate(cl_videostream); + cl_videoimagewidth = dpvsimpledecode_getwidth(cl_videostream); + cl_videoimageheight = dpvsimpledecode_getheight(cl_videostream); + + // RGBA format + cl_videoimagedata_bytesperpixel = 4; + cl_videoimagedata_rmask = BigLong(0xFF000000); + cl_videoimagedata_gmask = BigLong(0x00FF0000); + cl_videoimagedata_bmask = BigLong(0x0000FF00); + cl_videoimagedata = Mem_Alloc(clvideomempool, cl_videoimagewidth * cl_videoimageheight * cl_videoimagedata_bytesperpixel); + //memset(cl_videoimagedata, 97, cl_videoimagewidth * cl_videoimageheight * cl_videoimagedata_bytesperpixel); + + cl_videosoundrate = dpvsimpledecode_getsoundrate(cl_videostream); + cl_videosoundlength = 0; + cl_videosounddata = NULL; + cl_videosoundresamplelength = 0; + cl_videosoundresampledata = NULL; + + cl_videotexturepool = R_AllocTexturePool(); + cl_videotexture = R_LoadTexture(cl_videotexturepool, "videotexture", cl_videoimagewidth, cl_videoimageheight, NULL, TEXTYPE_RGBA, 0); +} + +void CL_VideoStop(void) +{ + cl_videoplaying = false; + + if (cl_videostream) + dpvsimpledecode_close(cl_videostream); + cl_videostream = NULL; + + if (cl_videoimagedata) + Mem_Free(cl_videoimagedata); + cl_videoimagedata = NULL; + + if (cl_videosounddata) + Mem_Free(cl_videosounddata); + cl_videosounddata = NULL; + + if (cl_videosoundresampledata) + Mem_Free(cl_videosoundresampledata); + cl_videosoundresampledata = NULL; + + cl_videotexture = NULL; + R_FreeTexturePool(&cl_videotexturepool); + + Draw_FreePic("engine_videoframe"); +} + +static void CL_PlayVideo_f(void) +{ + char name[1024]; + + if (Cmd_Argc() != 2) + { + Con_Printf ("usage: playvideo \nplays video named video/.dpv\n"); + return; + } + + sprintf(name, "%s/video/%s.dpv", com_gamedir, Cmd_Argv(1)); + CL_VideoStart(name); +} + +static void CL_StopVideo_f(void) +{ + CL_VideoStop(); +} + +void CL_Video_Init(void) +{ + Cmd_AddCommand("playvideo", CL_PlayVideo_f); + Cmd_AddCommand("stopvideo", CL_StopVideo_f); + + clvideomempool = Mem_AllocPool("CL_Video"); +} diff --git a/cl_video.h b/cl_video.h new file mode 100644 index 00000000..58738afc --- /dev/null +++ b/cl_video.h @@ -0,0 +1,12 @@ + +#ifndef CL_VIDEO_H +#define CL_VIDEO_H + +extern int cl_videoplaying; +void CL_VideoFrame(void); +void CL_DrawVideo(void); +void CL_VideoStart(char *filename); +void CL_VideoStop(void); +void CL_Video_Init(void); + +#endif diff --git a/darkplaces.dsp b/darkplaces.dsp index 5f373267..309fe914 100644 --- a/darkplaces.dsp +++ b/darkplaces.dsp @@ -7,14 +7,14 @@ CFG=darkplaces - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run -!MESSAGE +!MESSAGE !MESSAGE NMAKE /f "darkplaces.mak". -!MESSAGE +!MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE +!MESSAGE !MESSAGE NMAKE /f "darkplaces.mak" CFG="darkplaces - Win32 Debug" -!MESSAGE +!MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "darkplaces - Win32 Release" (based on "Win32 (x86) Application") @@ -148,6 +148,10 @@ SOURCE=.\cl_tent.c # End Source File # Begin Source File +SOURCE=.\cl_video.c +# End Source File +# Begin Source File + SOURCE=.\cmd.c # End Source File # Begin Source File @@ -176,6 +180,10 @@ SOURCE=.\cvar.c # End Source File # Begin Source File +SOURCE=.\dpvsimpledecode.c +# End Source File +# Begin Source File + SOURCE=.\filematch.c # End Source File # Begin Source File @@ -412,6 +420,10 @@ SOURCE=.\wad.c # End Source File # Begin Source File +SOURCE=.\wavefile.c +# End Source File +# Begin Source File + SOURCE=.\world.c # End Source File # Begin Source File @@ -456,6 +468,10 @@ SOURCE=.\cl_screen.h # End Source File # Begin Source File +SOURCE=.\cl_video.h +# End Source File +# Begin Source File + SOURCE=.\client.h # End Source File # Begin Source File @@ -488,6 +504,10 @@ SOURCE=.\cvar.h # End Source File # Begin Source File +SOURCE=.\dpvsimpledecode.h +# End Source File +# Begin Source File + SOURCE=.\draw.h # End Source File # Begin Source File @@ -676,6 +696,10 @@ SOURCE=.\wad.h # End Source File # Begin Source File +SOURCE=.\wavefile.h +# End Source File +# Begin Source File + SOURCE=.\winquake.h # End Source File # Begin Source File diff --git a/dpvsimpledecode.c b/dpvsimpledecode.c new file mode 100644 index 00000000..bdfcf319 --- /dev/null +++ b/dpvsimpledecode.c @@ -0,0 +1,708 @@ + +#include +#include +#include +#include +#include "dpvsimpledecode.h" +#include "wavefile.h" + +#define EMBEDDEDHZREAD 1 + +#ifndef EMBEDDEDHZREAD +#include "hz_read.h" +#include "hz_read.c" +#else +#define HZREADERROR_OK 0 +#define HZREADERROR_EOF 1 +#define HZREADERROR_MALLOCFAILED 2 + +#define HZREADBLOCKSIZE 16000 + +typedef struct +{ + FILE *file; + int endoffile; +} +hz_bitstream_read_t; + +typedef struct hz_bitstream_readblock_s +{ + struct hz_bitstream_readblock_s *next; + unsigned int size; + unsigned char data[HZREADBLOCKSIZE]; +} +hz_bitstream_readblock_t; + +typedef struct +{ + hz_bitstream_readblock_t *blocks; + hz_bitstream_readblock_t *current; + unsigned int position; + unsigned int store; + int count; +} +hz_bitstream_readblocks_t; + +hz_bitstream_read_t *hz_bitstream_read_open(char *filename) +{ + FILE *file; + hz_bitstream_read_t *stream; + if ((file = fopen(filename, "rb"))) + { + stream = malloc(sizeof(hz_bitstream_read_t)); + memset(stream, 0, sizeof(*stream)); + stream->file = file; + return stream; + } + else + return NULL; +} + +void hz_bitstream_read_close(hz_bitstream_read_t *stream) +{ + if (stream) + { + fclose(stream->file); + free(stream); + } +} + +unsigned int hz_bitstream_read_currentbyte(hz_bitstream_read_t *stream) +{ + return ftell(stream->file); +} + +int hz_bitstream_read_seek(hz_bitstream_read_t *stream, unsigned int position) +{ + stream->endoffile = 0; + return fseek(stream->file, position, SEEK_SET) != 0; +} + +hz_bitstream_readblocks_t *hz_bitstream_read_blocks_new(void) +{ + hz_bitstream_readblocks_t *blocks; + blocks = malloc(sizeof(hz_bitstream_readblocks_t)); + if (blocks == NULL) + return NULL; + memset(blocks, 0, sizeof(hz_bitstream_readblocks_t)); + return blocks; +} + +void hz_bitstream_read_blocks_free(hz_bitstream_readblocks_t *blocks) +{ + hz_bitstream_readblock_t *b, *n; + if (blocks == NULL) + return; + for (b = blocks->blocks;b;b = n) + { + n = b->next; + free(b); + } + free(blocks); +} + +void hz_bitstream_read_flushbits(hz_bitstream_readblocks_t *blocks) +{ + blocks->store = 0; + blocks->count = 0; +} + +int hz_bitstream_read_blocks_read(hz_bitstream_readblocks_t *blocks, hz_bitstream_read_t *stream, unsigned int size) +{ + int s; + hz_bitstream_readblock_t *b, *p; + s = size; + p = NULL; + b = blocks->blocks; + while (s > 0) + { + if (b == NULL) + { + b = malloc(sizeof(hz_bitstream_readblock_t)); + if (b == NULL) + return HZREADERROR_MALLOCFAILED; + b->next = NULL; + b->size = 0; + if (p != NULL) + p->next = b; + else + blocks->blocks = b; + } + if (s > HZREADBLOCKSIZE) + b->size = HZREADBLOCKSIZE; + else + b->size = s; + s -= b->size; + if (fread(b->data, 1, b->size, stream->file) != b->size) + { + stream->endoffile = 1; + break; + } + p = b; + b = b->next; + } + while (b) + { + b->size = 0; + b = b->next; + } + blocks->current = blocks->blocks; + blocks->position = 0; + hz_bitstream_read_flushbits(blocks); + if (stream->endoffile) + return HZREADERROR_EOF; + return HZREADERROR_OK; +} + +unsigned int hz_bitstream_read_blocks_getbyte(hz_bitstream_readblocks_t *blocks) +{ + while (blocks->current != NULL && blocks->position >= blocks->current->size) + { + blocks->position = 0; + blocks->current = blocks->current->next; + } + if (blocks->current == NULL) + return 0; + return blocks->current->data[blocks->position++]; +} + +int hz_bitstream_read_bit(hz_bitstream_readblocks_t *blocks) +{ + if (!blocks->count) + { + blocks->count += 8; + blocks->store <<= 8; + blocks->store |= hz_bitstream_read_blocks_getbyte(blocks) & 0xFF; + } + blocks->count--; + return (blocks->store >> blocks->count) & 1; +} + +unsigned int hz_bitstream_read_bits(hz_bitstream_readblocks_t *blocks, unsigned int size) +{ + unsigned int num = 0; + // we can only handle about 24 bits at a time safely + // (there might be up to 7 bits more than we need in the bit store) + if (size > 24) + { + size -= 8; + num |= hz_bitstream_read_bits(blocks, 8) << size; + } + while (blocks->count < size) + { + blocks->count += 8; + blocks->store <<= 8; + blocks->store |= hz_bitstream_read_blocks_getbyte(blocks) & 0xFF; + } + blocks->count -= size; + num |= (blocks->store >> blocks->count) & ((1 << size) - 1); + return num; +} + +unsigned int hz_bitstream_read_byte(hz_bitstream_readblocks_t *blocks) +{ + return hz_bitstream_read_blocks_getbyte(blocks); +} + +unsigned int hz_bitstream_read_short(hz_bitstream_readblocks_t *blocks) +{ + return (hz_bitstream_read_byte(blocks) << 8) + | (hz_bitstream_read_byte(blocks)); +} + +unsigned int hz_bitstream_read_int(hz_bitstream_readblocks_t *blocks) +{ + return (hz_bitstream_read_byte(blocks) << 24) + | (hz_bitstream_read_byte(blocks) << 16) + | (hz_bitstream_read_byte(blocks) << 8) + | (hz_bitstream_read_byte(blocks)); +} + +void hz_bitstream_read_bytes(hz_bitstream_readblocks_t *blocks, void *outdata, unsigned int size) +{ + unsigned char *out; + out = outdata; + while (size--) + *out++ = hz_bitstream_read_byte(blocks); +} +#endif + +#define BLOCKSIZE 8 + +typedef struct dpvsimpledecodestream_s +{ + hz_bitstream_read_t *bitstream; + hz_bitstream_readblocks_t *framedatablocks; + + int error; + + double info_framerate; + unsigned int info_frames; + + unsigned int info_imagewidth; + unsigned int info_imageheight; + unsigned int info_imagebpp; + unsigned int info_imageRloss; + unsigned int info_imageRmask; + unsigned int info_imageRshift; + unsigned int info_imageGloss; + unsigned int info_imageGmask; + unsigned int info_imageGshift; + unsigned int info_imageBloss; + unsigned int info_imageBmask; + unsigned int info_imageBshift; + unsigned int info_imagesize; + + // current video frame (needed because of delta compression) + int videoframenum; + // current video frame data (needed because of delta compression) + unsigned int *videopixels; + + // wav file the sound is being read from + wavefile_t *wavefile; +} +dpvsimpledecodestream_t; + +static int dpvsimpledecode_setpixelformat(dpvsimpledecodestream_t *s, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel) +{ + int Rshift, Rbits, Gshift, Gbits, Bshift, Bbits; + if (!Rmask) + { + s->error = DPVSIMPLEDECODEERROR_INVALIDRMASK; + return s->error; + } + if (!Gmask) + { + s->error = DPVSIMPLEDECODEERROR_INVALIDGMASK; + return s->error; + } + if (!Bmask) + { + s->error = DPVSIMPLEDECODEERROR_INVALIDBMASK; + return s->error; + } + if (Rmask & Gmask || Rmask & Bmask || Gmask & Bmask) + { + s->error = DPVSIMPLEDECODEERROR_COLORMASKSOVERLAP; + return s->error; + } + switch (bytesperpixel) + { + case 2: + if ((Rmask | Gmask | Bmask) > 65536) + { + s->error = DPVSIMPLEDECODEERROR_COLORMASKSEXCEEDBPP; + return s->error; + } + break; + case 4: + break; + default: + s->error = DPVSIMPLEDECODEERROR_UNSUPPORTEDBPP; + return s->error; + break; + } + for (Rshift = 0;!(Rmask & 1);Rshift++, Rmask >>= 1); + for (Gshift = 0;!(Gmask & 1);Gshift++, Gmask >>= 1); + for (Bshift = 0;!(Bmask & 1);Bshift++, Bmask >>= 1); + if (((Rmask + 1) & Rmask) != 0) + { + s->error = DPVSIMPLEDECODEERROR_INVALIDRMASK; + return s->error; + } + if (((Gmask + 1) & Gmask) != 0) + { + s->error = DPVSIMPLEDECODEERROR_INVALIDGMASK; + return s->error; + } + if (((Bmask + 1) & Bmask) != 0) + { + s->error = DPVSIMPLEDECODEERROR_INVALIDBMASK; + return s->error; + } + for (Rbits = 0;Rmask & 1;Rbits++, Rmask >>= 1); + for (Gbits = 0;Gmask & 1;Gbits++, Gmask >>= 1); + for (Bbits = 0;Bmask & 1;Bbits++, Bmask >>= 1); + if (Rbits > 8) + { + Rshift += (Rbits - 8); + Rbits = 8; + } + if (Gbits > 8) + { + Gshift += (Gbits - 8); + Gbits = 8; + } + if (Bbits > 8) + { + Bshift += (Bbits - 8); + Bbits = 8; + } + s->info_imagebpp = bytesperpixel; + s->info_imageRloss = 16 + (8 - Rbits); + s->info_imageGloss = 8 + (8 - Gbits); + s->info_imageBloss = 0 + (8 - Bbits); + s->info_imageRmask = (1 << Rbits) - 1; + s->info_imageGmask = (1 << Gbits) - 1; + s->info_imageBmask = (1 << Bbits) - 1; + s->info_imageRshift = Rshift; + s->info_imageGshift = Gshift; + s->info_imageBshift = Bshift; + s->info_imagesize = s->info_imagewidth * s->info_imageheight * s->info_imagebpp; + return s->error; +} + +// opening and closing streams + +static void StripExtension(char *in, char *out) +{ + char *dot, *c; + dot = NULL; + for (c = in;*c;c++) + { + if (*c == ':' || *c == '\\' || *c == '/') + dot = NULL; + if (*c == '.') + dot = c; + } + if (dot == NULL) + { + // nothing to remove + strcpy(out, in); + return; + } + else + { + memcpy(out, in, dot - in); + out[dot - in] = 0; + } +} + +// opens a stream +void *dpvsimpledecode_open(char *filename, char **errorstring) +{ + dpvsimpledecodestream_t *s; + char t[8], *wavename; + if (errorstring != NULL) + *errorstring = NULL; + s = malloc(sizeof(dpvsimpledecodestream_t)); + if (s != NULL) + { + s->bitstream = hz_bitstream_read_open(filename); + if (s->bitstream != NULL) + { + // check file identification + s->framedatablocks = hz_bitstream_read_blocks_new(); + if (s->framedatablocks != NULL) + { + hz_bitstream_read_blocks_read(s->framedatablocks, s->bitstream, 8); + hz_bitstream_read_bytes(s->framedatablocks, t, 8); + if (!memcmp(t, "DPVideo", 8)) + { + // check version number + hz_bitstream_read_blocks_read(s->framedatablocks, s->bitstream, 2); + if (hz_bitstream_read_short(s->framedatablocks) == 1) + { + hz_bitstream_read_blocks_read(s->framedatablocks, s->bitstream, 12); + s->info_imagewidth = hz_bitstream_read_short(s->framedatablocks); + s->info_imageheight = hz_bitstream_read_short(s->framedatablocks); + s->info_framerate = (double) hz_bitstream_read_int(s->framedatablocks) * (1.0 / 65536.0); + + if (s->info_framerate > 0.0) + { + s->videopixels = malloc(s->info_imagewidth * s->info_imageheight * sizeof(*s->videopixels)); + if (s->videopixels != NULL) + { + wavename = malloc(strlen(filename) + 10); + if (wavename) + { + StripExtension(filename, wavename); + strcat(wavename, ".wav"); + s->wavefile = waveopen(wavename, NULL); + free(wavename); + } + // all is well... + s->videoframenum = -10000; + return s; + } + else if (errorstring != NULL) + *errorstring = "unable to allocate video image buffer"; + } + else if (errorstring != NULL) + *errorstring = "error in video info chunk"; + } + else if (errorstring != NULL) + *errorstring = "read error"; + } + else if (errorstring != NULL) + *errorstring = "not a dpvideo file"; + hz_bitstream_read_blocks_free(s->framedatablocks); + } + else if (errorstring != NULL) + *errorstring = "unable to allocate memory for reading buffer"; + hz_bitstream_read_close(s->bitstream); + } + else if (errorstring != NULL) + *errorstring = "unable to open file"; + free(s); + } + else if (errorstring != NULL) + *errorstring = "unable to allocate memory for stream info structure"; + return NULL; +} + +// closes a stream +void dpvsimpledecode_close(void *stream) +{ + dpvsimpledecodestream_t *s = stream; + if (s == NULL) + return; + if (s->videopixels) + free(s->videopixels); + if (s->wavefile) + waveclose(s->wavefile); + if (s->framedatablocks) + hz_bitstream_read_blocks_free(s->framedatablocks); + if (s->bitstream) + hz_bitstream_read_close(s->bitstream); + free(s); +} + +// utilitarian functions + +// returns the current error number for the stream, and resets the error +// number to DPVSIMPLEDECODEERROR_NONE +// if the supplied string pointer variable is not NULL, it will be set to the +// error message +int dpvsimpledecode_error(void *stream, char **errorstring) +{ + dpvsimpledecodestream_t *s = stream; + int e; + e = s->error; + s->error = 0; + if (errorstring) + { + switch (e) + { + case DPVSIMPLEDECODEERROR_NONE: + *errorstring = "no error"; + break; + case DPVSIMPLEDECODEERROR_EOF: + *errorstring = "end of file reached (this is not an error)"; + break; + case DPVSIMPLEDECODEERROR_READERROR: + *errorstring = "read error (corrupt or incomplete file)"; + break; + case DPVSIMPLEDECODEERROR_SOUNDBUFFERTOOSMALL: + *errorstring = "sound buffer is too small for decoding frame (please allocate it as large as dpvsimpledecode_getneededsoundbufferlength suggests)"; + break; + case DPVSIMPLEDECODEERROR_INVALIDRMASK: + *errorstring = "invalid red bits mask"; + break; + case DPVSIMPLEDECODEERROR_INVALIDGMASK: + *errorstring = "invalid green bits mask"; + break; + case DPVSIMPLEDECODEERROR_INVALIDBMASK: + *errorstring = "invalid blue bits mask"; + break; + case DPVSIMPLEDECODEERROR_COLORMASKSOVERLAP: + *errorstring = "color bit masks overlap"; + break; + case DPVSIMPLEDECODEERROR_COLORMASKSEXCEEDBPP: + *errorstring = "color masks too big for specified bytes per pixel"; + break; + case DPVSIMPLEDECODEERROR_UNSUPPORTEDBPP: + *errorstring = "unsupported bytes per pixel (must be 2 for 16bit, or 4 for 32bit)"; + break; + default: + *errorstring = "unknown error"; + break; + } + } + return e; +} + +// returns the width of the image data +unsigned int dpvsimpledecode_getwidth(void *stream) +{ + dpvsimpledecodestream_t *s = stream; + return s->info_imagewidth; +} + +// returns the height of the image data +unsigned int dpvsimpledecode_getheight(void *stream) +{ + dpvsimpledecodestream_t *s = stream; + return s->info_imageheight; +} + +// returns the sound sample rate of the stream +unsigned int dpvsimpledecode_getsoundrate(void *stream) +{ + dpvsimpledecodestream_t *s = stream; + if (s->wavefile) + return s->wavefile->info_rate; + else + return 0; +} + +// returns the framerate of the stream +double dpvsimpledecode_getframerate(void *stream) +{ + dpvsimpledecodestream_t *s = stream; + return s->info_framerate; +} + + + + + +static int dpvsimpledecode_convertpixels(dpvsimpledecodestream_t *s, void *imagedata, int imagebytesperrow) +{ + unsigned int a, x, y, width, height; + unsigned int Rloss, Rmask, Rshift, Gloss, Gmask, Gshift, Bloss, Bmask, Bshift; + unsigned int *in; + + width = s->info_imagewidth; + height = s->info_imageheight; + + Rloss = s->info_imageRloss; + Rmask = s->info_imageRmask; + Rshift = s->info_imageRshift; + Gloss = s->info_imageGloss; + Gmask = s->info_imageGmask; + Gshift = s->info_imageGshift; + Bloss = s->info_imageBloss; + Bmask = s->info_imageBmask; + Bshift = s->info_imageBshift; + + in = s->videopixels; + if (s->info_imagebpp == 4) + { + unsigned int *outrow; + for (y = 0;y < height;y++) + { + outrow = (void *)((unsigned char *)imagedata + y * imagebytesperrow); + for (x = 0;x < width;x++) + { + a = *in++; + outrow[x] = (((a >> Rloss) & Rmask) << Rshift) | (((a >> Gloss) & Gmask) << Gshift) | (((a >> Bloss) & Bmask) << Bshift); + } + } + } + else + { + unsigned short *outrow; + for (y = 0;y < height;y++) + { + outrow = (void *)((unsigned char *)imagedata + y * imagebytesperrow); + if (Rloss == 19 && Gloss == 10 && Bloss == 3 && Rshift == 11 && Gshift == 5 && Bshift == 0) + { + // optimized + for (x = 0;x < width;x++) + { + a = *in++; + outrow[x] = ((a >> 8) & 0xF800) | ((a >> 5) & 0x07E0) | ((a >> 3) & 0x001F); + } + } + else + { + for (x = 0;x < width;x++) + { + a = *in++; + outrow[x] = (((a >> Rloss) & Rmask) << Rshift) | (((a >> Gloss) & Gmask) << Gshift) | (((a >> Bloss) & Bmask) << Bshift); + } + } + } + } + return s->error; +} + +static int dpvsimpledecode_decompressimage(dpvsimpledecodestream_t *s) +{ + int i, a, b, colors, g, x1, y1, bw, bh, width, height, palettebits; + unsigned int palette[256], *outrow, *out; + g = BLOCKSIZE; + width = s->info_imagewidth; + height = s->info_imageheight; + for (y1 = 0;y1 < height;y1 += g) + { + outrow = s->videopixels + y1 * width; + bh = g; + if (y1 + bh > height) + bh = height - y1; + for (x1 = 0;x1 < width;x1 += g) + { + out = outrow + x1; + bw = g; + if (x1 + bw > width) + bw = width - x1; + if (hz_bitstream_read_bit(s->framedatablocks)) + { + // updated block + palettebits = hz_bitstream_read_bits(s->framedatablocks, 3); + colors = 1 << palettebits; + for (i = 0;i < colors;i++) + palette[i] = hz_bitstream_read_bits(s->framedatablocks, 24); + if (palettebits) + { + for (b = 0;b < bh;b++, out += width) + for (a = 0;a < bw;a++) + out[a] = palette[hz_bitstream_read_bits(s->framedatablocks, palettebits)]; + } + else + { + for (b = 0;b < bh;b++, out += width) + for (a = 0;a < bw;a++) + out[a] = palette[0]; + } + } + } + } + return s->error; +} + +// decodes a video frame to the supplied output pixels +int dpvsimpledecode_video(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow) +{ + dpvsimpledecodestream_t *s = stream; + unsigned int framedatasize; + char t[4]; + s->error = DPVSIMPLEDECODEERROR_NONE; + if (dpvsimpledecode_setpixelformat(s, Rmask, Gmask, Bmask, bytesperpixel)) + return s->error; + + hz_bitstream_read_blocks_read(s->framedatablocks, s->bitstream, 8); + hz_bitstream_read_bytes(s->framedatablocks, t, 4); + if (memcmp(t, "VID0", 4)) + { + if (t[0] == 0) + return (s->error = DPVSIMPLEDECODEERROR_EOF); + else + return (s->error = DPVSIMPLEDECODEERROR_READERROR); + } + framedatasize = hz_bitstream_read_int(s->framedatablocks); + hz_bitstream_read_blocks_read(s->framedatablocks, s->bitstream, framedatasize); + if (dpvsimpledecode_decompressimage(s)) + return s->error; + + dpvsimpledecode_convertpixels(s, imagedata, imagebytesperrow); + return s->error; +} + +// (note: sound is 16bit stereo native-endian, left channel first) +int dpvsimpledecode_audio(void *stream, short *soundbuffer, int requestedlength) +{ + int samples; + dpvsimpledecodestream_t *s = stream; + s->error = DPVSIMPLEDECODEERROR_NONE; + if (requestedlength) + { + samples = 0; + if (s->wavefile && requestedlength) + samples = waveread16stereo(s->wavefile, soundbuffer, requestedlength); + if (samples < requestedlength) + memset(soundbuffer + samples * 2, 0, (requestedlength - samples) * sizeof(short[2])); + } + return s->error; +} diff --git a/dpvsimpledecode.h b/dpvsimpledecode.h new file mode 100644 index 00000000..36e61a41 --- /dev/null +++ b/dpvsimpledecode.h @@ -0,0 +1,49 @@ + +#ifndef DPVSIMPLEDECODE_H +#define DPVSIMPLEDECODE_H + +#define DPVSIMPLEDECODEERROR_NONE 0 +#define DPVSIMPLEDECODEERROR_EOF 1 +#define DPVSIMPLEDECODEERROR_READERROR 2 +#define DPVSIMPLEDECODEERROR_SOUNDBUFFERTOOSMALL 3 +#define DPVSIMPLEDECODEERROR_INVALIDRMASK 4 +#define DPVSIMPLEDECODEERROR_INVALIDGMASK 5 +#define DPVSIMPLEDECODEERROR_INVALIDBMASK 6 +#define DPVSIMPLEDECODEERROR_COLORMASKSOVERLAP 7 +#define DPVSIMPLEDECODEERROR_COLORMASKSEXCEEDBPP 8 +#define DPVSIMPLEDECODEERROR_UNSUPPORTEDBPP 9 + +// opening and closing streams + +// opens a stream +void *dpvsimpledecode_open(char *filename, char **errorstring); +// closes a stream +void dpvsimpledecode_close(void *stream); + +// utilitarian functions + +// returns the current error number for the stream, and resets the error +// number to DPVDECODEERROR_NONE +// if the supplied string pointer variable is not NULL, it will be set to the +// error message +int dpvsimpledecode_error(void *stream, char **errorstring); + +// returns the width of the image data +unsigned int dpvsimpledecode_getwidth(void *stream); + +// returns the height of the image data +unsigned int dpvsimpledecode_getheight(void *stream); + +// returns the sound sample rate of the stream +unsigned int dpvsimpledecode_getsoundrate(void *stream); + +// returns the framerate of the stream +double dpvsimpledecode_getframerate(void *stream); + +// decodes a video frame to the supplied output pixels +int dpvsimpledecode_video(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow); +// reads some sound +// (note: sound is 16bit stereo native-endian, left channel first) +int dpvsimpledecode_audio(void *stream, short *soundbuffer, int requestedlength); + +#endif diff --git a/draw.h b/draw.h index 085259cb..bbf5a21b 100644 --- a/draw.h +++ b/draw.h @@ -40,6 +40,10 @@ cachepic_t; void Draw_Init (void); cachepic_t *Draw_CachePic (char *path); +// create or update a pic's image +cachepic_t *Draw_NewPic(char *picname, int width, int height, int alpha, qbyte *pixels); +// free the texture memory used by a pic +void Draw_FreePic(char *picname); void R_DrawQueue(void); diff --git a/gl_draw.c b/gl_draw.c index de361fb0..3e1e2187 100644 --- a/gl_draw.c +++ b/gl_draw.c @@ -259,6 +259,67 @@ cachepic_t *Draw_CachePic (char *path) return pic; } +cachepic_t *Draw_NewPic(char *picname, int width, int height, int alpha, qbyte *pixels) +{ + int crc, hashkey; + cachepic_t *pic; + + crc = CRC_Block(picname, strlen(picname)); + hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE; + for (pic = cachepichash[hashkey];pic;pic = pic->chain) + if (!strcmp (picname, pic->name)) + break; + + if (pic) + { + if (pic->tex && R_TextureWidth(pic->tex) == width && R_TextureHeight(pic->tex) == height && (R_TextureHasAlpha(pic->tex) != 0) == (alpha != 0)) + { + R_UpdateTexture(pic->tex, pixels); + return pic; + } + } + else + { + if (pic == NULL) + { + if (numcachepics == MAX_CACHED_PICS) + Sys_Error ("numcachepics == MAX_CACHED_PICS"); + pic = cachepics + (numcachepics++); + strcpy (pic->name, picname); + // link into list + pic->chain = cachepichash[hashkey]; + cachepichash[hashkey] = pic; + } + } + + pic->width = width; + pic->height = height; + if (pic->tex) + R_FreeTexture(pic->tex); + pic->tex = R_LoadTexture (drawtexturepool, picname, width, height, pixels, TEXTYPE_RGBA, alpha ? TEXF_ALPHA : 0); + return pic; +} + +void Draw_FreePic(char *picname) +{ + int crc; + int hashkey; + cachepic_t *pic; + // this doesn't really free the pic, but does free it's texture + crc = CRC_Block(picname, strlen(picname)); + hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE; + for (pic = cachepichash[hashkey];pic;pic = pic->chain) + { + if (!strcmp (picname, pic->name)) + { + R_FreeTexture(pic->tex); + pic->width = 0; + pic->height = 0; + return; + } + } +} + /* =============== Draw_Init @@ -296,6 +357,7 @@ void GL_Draw_Init (void) R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap); } +extern cvar_t gl_mesh_drawmode; void R_DrawQueue(void) { int pos, num, chartexnum, overbright; @@ -305,6 +367,7 @@ void R_DrawQueue(void) char *str, *currentpic; int batch, batchcount, additive; unsigned int color; + drawqueuemesh_t *mesh; if (!r_render.integer) return; @@ -479,6 +542,78 @@ void R_DrawQueue(void) x += w; } break; + case DRAWQUEUE_MESH: + if (batch) + { + batch = false; + qglEnd(); + } + mesh = (void *)(dq + 1); + qglBindTexture(GL_TEXTURE_2D, R_GetTexture(mesh->texture)); + if (gl_mesh_drawmode.integer > 0) + { + qglVertexPointer(3, GL_FLOAT, sizeof(float[3]), mesh->vertices);CHECKGLERROR + qglTexCoordPointer(2, GL_FLOAT, sizeof(float[2]), mesh->texcoords);CHECKGLERROR + qglColorPointer(4, GL_UNSIGNED_BYTE, sizeof(qbyte[4]), mesh->colors);CHECKGLERROR + qglEnableClientState(GL_VERTEX_ARRAY);CHECKGLERROR + qglEnableClientState(GL_TEXTURE_COORD_ARRAY);CHECKGLERROR + qglEnableClientState(GL_COLOR_ARRAY);CHECKGLERROR + } + if (gl_mesh_drawmode.integer == 3 && qglDrawRangeElements == NULL) + Cvar_SetValueQuick(&gl_mesh_drawmode, 2); + + if (gl_mesh_drawmode.integer == 3) + { + // GL 1.2 or GL 1.1 with extension + qglDrawRangeElements(GL_TRIANGLES, 0, mesh->numvertices, mesh->numindices, GL_UNSIGNED_INT, mesh->indices); + CHECKGLERROR + } + else if (gl_mesh_drawmode.integer == 2) + { + // GL 1.1 + qglDrawElements(GL_TRIANGLES, mesh->numindices, GL_UNSIGNED_INT, mesh->indices); + CHECKGLERROR + } + else if (gl_mesh_drawmode.integer == 1) + { + int i; + // GL 1.1 + // feed it manually using glArrayElement + qglBegin(GL_TRIANGLES); + for (i = 0;i < mesh->numindices;i++) + qglArrayElement(mesh->indices[i]); + qglEnd(); + CHECKGLERROR + } + else + { + int i, in; + // GL 1.1 but not using vertex arrays - 3dfx glquake minigl driver + // feed it manually + if (gl_mesh_drawmode.integer != 0) + Cvar_SetValueQuick(&gl_mesh_drawmode, 0); + qglBegin(GL_TRIANGLES); + for (i = 0;i < mesh->numindices;i++) + { + in = mesh->indices[i]; + qglColor4ub(mesh->colors[in * 4], mesh->colors[in * 4 + 1], mesh->colors[in * 4 + 2], mesh->colors[in * 4 + 3]); + qglTexCoord2f(mesh->texcoords[in * 2], mesh->texcoords[in * 2 + 1]); + qglVertex3f(mesh->vertices[in * 3], mesh->vertices[in * 3 + 1], mesh->vertices[in * 3 + 2]); + } + qglEnd(); + CHECKGLERROR + } + if (gl_mesh_drawmode.integer > 0) + { + qglDisableClientState(GL_VERTEX_ARRAY);CHECKGLERROR + qglDisableClientState(GL_TEXTURE_COORD_ARRAY);CHECKGLERROR + qglDisableClientState(GL_COLOR_ARRAY);CHECKGLERROR + } + // restore color, since it got trashed by using color array + qglColor4ub((qbyte)(((color >> 24) & 0xFF) >> overbright), (qbyte)(((color >> 16) & 0xFF) >> overbright), (qbyte)(((color >> 8) & 0xFF) >> overbright), (qbyte)(color & 0xFF)); + CHECKGLERROR + currentpic = "\0"; + break; } } if (batch) diff --git a/gl_textures.c b/gl_textures.c index 43bf88e8..a2b005fa 100644 --- a/gl_textures.c +++ b/gl_textures.c @@ -201,12 +201,13 @@ int R_GetTexture(rtexture_t *rt) return glt->image->texnum; } -static void R_FreeTexture(gltexture_t *glt) +void R_FreeTexture(rtexture_t *rt) { - gltexture_t **gltpointer; + gltexture_t *glt, **gltpointer; gltextureimage_t *image, **gltimagepointer; GLuint texnum; + glt = (gltexture_t *)rt; if (glt == NULL) Host_Error("R_FreeTexture: texture == NULL\n"); @@ -293,7 +294,7 @@ void R_FreeTexturePool(rtexturepool_t **rtexturepool) else Host_Error("R_FreeTexturePool: pool not linked\n"); while (pool->gltchain) - R_FreeTexture(pool->gltchain); + R_FreeTexture((rtexture_t *)pool->gltchain); if (pool->imagechain) Sys_Error("R_FreeTexturePool: not all images freed\n"); Mem_Free(pool); @@ -924,7 +925,7 @@ rtexture_t *R_LoadTexture (rtexturepool_t *rtexturepool, char *identifier, int w return (rtexture_t *)glt; // exact match, use existing } Con_Printf("R_LoadTexture: cache mismatch on %s, replacing old texture\n", identifier); - R_FreeTexture(glt); + R_FreeTexture((rtexture_t *)glt); } return (rtexture_t *)R_SetupTexture(pool, identifier, crc, width, height, flags | GLTEXF_UPLOAD, texinfo, data, NULL, NULL, 0); @@ -958,7 +959,7 @@ rtexture_t *R_ProceduralTexture (rtexturepool_t *rtexturepool, char *identifier, return (rtexture_t *)glt; // exact match, use existing } Con_Printf("R_LoadTexture: cache mismatch, replacing old texture\n"); - R_FreeTexture(glt); + R_FreeTexture((rtexture_t *)glt); } return (rtexture_t *)R_SetupTexture(pool, identifier, 0, width, height, flags | GLTEXF_PROCEDURAL | GLTEXF_UPLOAD, texinfo, NULL, generate, proceduraldata, proceduraldatasize); diff --git a/host.c b/host.c index 33797065..c6ffa4aa 100644 --- a/host.c +++ b/host.c @@ -19,8 +19,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // host.c -- coordinates spawning and killing of local servers -#include "quakedef.h" #include +#include "quakedef.h" +#include "cl_video.h" /* @@ -716,6 +717,8 @@ void _Host_Frame (float time) ui_update(); + CL_VideoFrame(); + // update video if (host_speeds.integer) time1 = Sys_DoubleTime (); diff --git a/makefile b/makefile index 5538b3f0..8feb568d 100644 --- a/makefile +++ b/makefile @@ -22,7 +22,7 @@ SOUNDLIB= #if you want no CD audio CD=cd_null.o -OBJECTS= builddate.o $(CD) $(SND) chase.o cl_demo.o cl_input.o cl_main.o cl_parse.o cl_tent.o cmd.o common.o console.o crc.o cvar.o fractalnoise.o gl_draw.o r_sky.o gl_rmain.o gl_rsurf.o host.o host_cmd.o image.o keys.o mathlib.o menu.o model_alias.o model_brush.o model_shared.o model_sprite.o net_bsd.o net_udp.o net_dgrm.o net_loop.o net_main.o pr_cmds.o pr_edict.o pr_exec.o r_light.o r_explosion.o sbar.o sv_main.o sv_move.o sv_phys.o sv_user.o sv_light.o sys_linux.o transform.o view.o wad.o world.o zone.o vid_shared.o palette.o r_crosshairs.o gl_textures.o gl_models.o r_sprites.o r_modules.o r_explosion.o r_lerpanim.o protocol.o quakeio.o r_clip.o ui.o portals.o sys_shared.o cl_light.o gl_backend.o cl_particles.o cl_screen.o cgamevm.o cgame.o filematch.o collision.o cl_collision.o matrixlib.o +OBJECTS= builddate.o $(CD) $(SND) chase.o cl_demo.o cl_input.o cl_main.o cl_parse.o cl_tent.o cmd.o common.o console.o crc.o cvar.o fractalnoise.o gl_draw.o r_sky.o gl_rmain.o gl_rsurf.o host.o host_cmd.o image.o keys.o mathlib.o menu.o model_alias.o model_brush.o model_shared.o model_sprite.o net_bsd.o net_udp.o net_dgrm.o net_loop.o net_main.o pr_cmds.o pr_edict.o pr_exec.o r_light.o r_explosion.o sbar.o sv_main.o sv_move.o sv_phys.o sv_user.o sv_light.o sys_linux.o transform.o view.o wad.o world.o zone.o vid_shared.o palette.o r_crosshairs.o gl_textures.o gl_models.o r_sprites.o r_modules.o r_explosion.o r_lerpanim.o protocol.o quakeio.o r_clip.o ui.o portals.o sys_shared.o cl_light.o gl_backend.o cl_particles.o cl_screen.o cgamevm.o cgame.o filematch.o collision.o cl_collision.o matrixlib.o cl_video.o dpvsimpledecode.o wavefile.o #K6/athlon optimizations CPUOPTIMIZATIONS=-march=k6 diff --git a/makefile.mingw b/makefile.mingw index 97ef1314..d7123629 100644 --- a/makefile.mingw +++ b/makefile.mingw @@ -1,5 +1,5 @@ -OBJECTS= builddate.o chase.o cl_demo.o cl_input.o cl_main.o cl_parse.o cl_tent.o cmd.o common.o console.o crc.o cvar.o fractalnoise.o gl_draw.o r_sky.o gl_rmain.o gl_rsurf.o host.o host_cmd.o image.o keys.o mathlib.o menu.o model_alias.o model_brush.o model_shared.o model_sprite.o net_dgrm.o net_loop.o net_main.o pr_cmds.o pr_edict.o pr_exec.o r_light.o r_explosion.o sbar.o snd_dma.o snd_mem.o snd_mix.o sv_main.o sv_move.o sv_phys.o sv_user.o sv_light.o transform.o view.o wad.o world.o zone.o vid_shared.o palette.o r_crosshairs.o gl_textures.o gl_models.o r_sprites.o r_modules.o r_explosion.o r_lerpanim.o protocol.o quakeio.o r_clip.o ui.o portals.o sys_shared.o cl_light.o gl_backend.o cl_particles.o cl_screen.o cgamevm.o cgame.o filematch.o collision.o cl_collision.o matrixlib.o +OBJECTS= builddate.o chase.o cl_demo.o cl_input.o cl_main.o cl_parse.o cl_tent.o cmd.o common.o console.o crc.o cvar.o fractalnoise.o gl_draw.o r_sky.o gl_rmain.o gl_rsurf.o host.o host_cmd.o image.o keys.o mathlib.o menu.o model_alias.o model_brush.o model_shared.o model_sprite.o net_dgrm.o net_loop.o net_main.o pr_cmds.o pr_edict.o pr_exec.o r_light.o r_explosion.o sbar.o snd_dma.o snd_mem.o snd_mix.o sv_main.o sv_move.o sv_phys.o sv_user.o sv_light.o transform.o view.o wad.o world.o zone.o vid_shared.o palette.o r_crosshairs.o gl_textures.o gl_models.o r_sprites.o r_modules.o r_explosion.o r_lerpanim.o protocol.o quakeio.o r_clip.o ui.o portals.o sys_shared.o cl_light.o gl_backend.o cl_particles.o cl_screen.o cgamevm.o cgame.o filematch.o collision.o cl_collision.o matrixlib.o cl_video.o dpvsimpledecode.o wavefile.o #K6/athlon optimizations #CPUOPTIMIZATIONS=-march=k6 diff --git a/menu.c b/menu.c index fbe9413d..3ab73593 100644 --- a/menu.c +++ b/menu.c @@ -224,12 +224,6 @@ void M_BuildTranslationTable(int top, int bottom) } -void M_DrawPicTranslate (float cx, float cy, char *picname) -{ - DrawQ_PicTranslate (menu_x + cx, menu_y + cy, picname, translationTable); -} - - void M_DrawTextBox (float x, float y, float width, float height) { int n; @@ -933,6 +927,28 @@ void M_Menu_Setup_f (void) setup_bottom = setup_oldbottom = cl_color.integer & 15; } +// LordHavoc: rewrote this code greatly +void M_MenuPlayerTranslate (qbyte *translation) +{ + int i, c; + unsigned int trans[4096]; + qpic_t *p; + + p = W_GetLumpName ("gfx/menuplyr.lmp"); + if (!p) + return; + c = p->width * p->height; + if (c > 4096) + { + Con_Printf("M_MenuPlayerTranslate: image larger than 4096 pixel buffer\n"); + return; + } + + for (i = 0;i < c;i++) + trans[i] = d_8to24table[translation[((qbyte *)p->data)[i]]]; + + Draw_NewPic("gfx/menuplyr.lmp", p->width, p->height, true, (qbyte *)trans); +} void M_Setup_Draw (void) { @@ -957,8 +973,11 @@ void M_Setup_Draw (void) M_Print (72, 140, "Accept Changes"); M_DrawPic (160, 64, "gfx/bigbox.lmp"); - M_BuildTranslationTable(setup_top*16, setup_bottom*16); - M_DrawPicTranslate (172, 72, "gfx/menuplyr.lmp"); + + // LordHavoc: rewrote this code greatly + M_BuildTranslationTable (setup_top*16, setup_bottom*16); + M_MenuPlayerTranslate (translationTable); + M_DrawPic (172, 72, "gfx/menuplyr.lmp"); M_DrawCharacter (56, setup_cursor_table [setup_cursor], 12+((int)(realtime*4)&1)); diff --git a/r_textures.h b/r_textures.h index 3abbe583..d0646392 100644 --- a/r_textures.h +++ b/r_textures.h @@ -53,6 +53,9 @@ rtexture_t *R_LoadTexture (rtexturepool_t *rtexturepool, char *identifier, int w // a procedurally generated texture, often animated over time, note: generate can be NULL (for odd uses) rtexture_t *R_ProceduralTexture (rtexturepool_t *rtexturepool, char *identifier, int width, int height, int textype, int flags, int (*generate)(qbyte *buffer, int width, int height, void *parameterdata, int parameterdatasize), void *parameterdata, int parameterdatasize); +// free a texture +void R_FreeTexture(rtexture_t *rt); + // update the image data of a texture, used by lightmap updates and procedural // textures. void R_UpdateTexture(rtexture_t *rt, qbyte *data); diff --git a/snd_dma.c b/snd_dma.c index 836227e0..096fdfb6 100644 --- a/snd_dma.c +++ b/snd_dma.c @@ -1086,7 +1086,7 @@ void S_RawSamples_Dequeue(int *samples, unsigned int length) if (l < length) { memset(samples + l * 2, 0, (length - l) * sizeof(int[2])); - //Con_Printf("S_RawSamples_Dequeue: padding with %i\n", length - l); + //Con_Printf("S_RawSamples_Dequeue: padding with %i samples\n", length - l); } s_rawsamplesbuffer_start = (s_rawsamplesbuffer_start + l) % RAWSAMPLESBUFFER; s_rawsamplesbuffer_count -= l; @@ -1098,3 +1098,40 @@ void S_RawSamples_ClearQueue(void) s_rawsamplesbuffer_start = 0; } +int S_RawSamples_QueueWantsMore(void) +{ + if (s_rawsamplesbuffer_count < min(shm->speed >> 1, RAWSAMPLESBUFFER >> 1)) + return RAWSAMPLESBUFFER - s_rawsamplesbuffer_count; + else + return 0; +} + +void S_ResampleBuffer16Stereo(short *input, int inputlength, short *output, int outputlength) +{ + if (inputlength != outputlength) + { + int i, position, stopposition, step; + short *in, *out; + step = (float) inputlength * 256.0f / (float) outputlength; + position = 0; + stopposition = (inputlength - 1) << 8; + out = output; + for (i = 0;i < outputlength && position < stopposition;i++, position += step) + { + in = input + ((position >> 8) << 1); + out[0] = (((in[1] - in[0]) * (position & 255)) >> 8) + in[0]; + out[1] = (((in[3] - in[2]) * (position & 255)) >> 8) + in[2]; + out += 2; + } + stopposition = inputlength << 8; + for (i = 0;i < outputlength && position < stopposition;i++, position += step) + { + in = input + ((position >> 8) << 1); + out[0] = in[0]; + out[1] = in[2]; + out += 2; + } + } + else + memcpy(output, input, inputlength * sizeof(short[2])); +} diff --git a/sound.h b/sound.h index 5ab1c4d8..561272ba 100644 --- a/sound.h +++ b/sound.h @@ -186,6 +186,11 @@ void S_RawSamples_Enqueue(short *samples, unsigned int length); void S_RawSamples_Dequeue(int *samples, unsigned int length); // empty the rawsamples queue void S_RawSamples_ClearQueue(void); +// returns how much more data the queue wants, or 0 if it is already full enough +int S_RawSamples_QueueWantsMore(void); + +// resamples one sound buffer into another, while changing the length +void S_ResampleBuffer16Stereo(short *input, int inputlength, short *output, int outputlength); #endif diff --git a/wavefile.c b/wavefile.c new file mode 100644 index 00000000..eae62d67 --- /dev/null +++ b/wavefile.c @@ -0,0 +1,193 @@ + +#include +#include +#include "wavefile.h" + +wavefile_t *waveopen(char *filename, char **errorstring) +{ + int validfmt, position, length, l; + char *error; + wavefile_t *w; + FILE *file; + unsigned char buffer[1024]; + error = NULL; + file = fopen(filename, "rb"); + if (file) + { + w = malloc(sizeof(*w)); + memset(w, 0, sizeof(*w)); + if (w) + { + w->file = file; + if (fread(buffer, 12, 1, w->file)) + { + if (!memcmp(buffer, "RIFF", 4)) + { + if (!memcmp(buffer + 8, "WAVE", 4)) + { + validfmt = 0; + for(;;) + { + if (!fread(buffer, 8, 1, w->file)) + { + //error = "error reading chunk\n"); + break; + } + position = ftell(w->file); + length = buffer[4] | (buffer[5] << 8) | (buffer[6] << 16) | (buffer[7] << 24); + if (!memcmp(buffer, "fmt ", 4)) + { + validfmt = 0; + l = length; + if (l > 16) + l = 16; + if (!fread(buffer, l, 1, w->file)) + { + error = "error reading \"fmt \" chunk\n"; + break; + } + w->info_format = buffer[0] | (buffer[1] << 8); + if (w->info_format != 1) + { + error = "only PCM format supported\n"; + break; + } + w->info_channels = buffer[2] | (buffer[3] << 8); + if (w->info_channels != 1 && w->info_channels != 2) + { + error = "only mono and stereo supported\n"; + break; + } + w->info_rate = buffer[4] | (buffer[5] << 8) | (buffer[6] << 16) | (buffer[7] << 24); + if (w->info_rate < 1) + { + error = "only rates 1hz-100khz supported\n"; + break; + } + w->info_bits = buffer[14] | (buffer[15] << 8); + if (w->info_bits != 8 && w->info_bits != 16) + { + error = "only 8bit and 16bit supported\n"; + break; + } + validfmt = 1; + } + else if (!memcmp(buffer, "data", 4)) + { + if (validfmt) + { + w->datalength = length; + w->dataposition = position; + } + } + // other chunks that might be of interest: + // "cue " (for looping) + if (fseek(w->file, position + length, SEEK_SET)) + { + error = "error seeking to next chunk\n"; + break; + } + } + if (w->datalength && validfmt) + { + w->info_bytesperchannel = w->info_bits / 8; + w->info_bytespersample = w->info_channels * w->info_bytesperchannel; + w->length = w->datalength / w->info_bytespersample; + w->position = 0; + fseek(w->file, w->dataposition, SEEK_SET); + return w; + } + } + else + error = "not a RIFF WAVE file\n"; + } + else + error = "not a RIFF file\n"; + } + else + error = "error reading file\n"; + free(w); + } + else + error = "unable to allocate memory\n"; + fclose(file); + } + else + error = "unable to open file\n"; + if (errorstring) + *errorstring = error; + return NULL; +} + +void waveclose(wavefile_t *f) +{ + if (f) + { + fclose(f->file); + free(f); + } +} + +unsigned int waveread16stereo(wavefile_t *w, short *soundbuffer, unsigned int samples) +{ + int i; + int length; + unsigned char *in; + short *out; + length = samples; + if (length > w->length - w->position) + length = w->length - w->position; + if (w->bufferlength < length) + { + if (w->buffer) + free(w->buffer); + w->bufferlength = length + 100; + w->buffer = malloc(w->bufferlength * w->info_bytespersample); + } + length = fread(w->buffer, w->info_bytespersample, length, w->file); + w->position += length; + if (length > 0) + { + if (w->info_bytesperchannel == 2) + { + if (w->info_channels == 2) + { + for (i = 0, in = w->buffer, out = soundbuffer;i < length;i++, in += 4, out += 2) + { + out[0] = in[0] | (in[1] << 8); + out[1] = in[2] | (in[3] << 8); + } + } + else + for (i = 0, in = w->buffer, out = soundbuffer;i < length;i++, in += 2, out += 2) + out[0] = out[1] = in[0] | (in[1] << 8); + } + else + { + if (w->info_channels == 2) + { + for (i = 0, in = w->buffer, out = soundbuffer;i < length;i++, in += 2, out += 2) + { + out[0] = (in[0] - 128) << 8; + out[1] = (in[1] - 128) << 8; + } + } + else + for (i = 0, in = w->buffer, out = soundbuffer;i < length;i++, in += 1, out += 2) + out[0] = out[1] = (in[0] - 128) << 8; + } + } + return length; +} + +unsigned int waveseek(wavefile_t *w, unsigned int samples) +{ + if (samples > w->datalength) + return 1; + else + { + w->position = samples; + fseek(w->file, w->dataposition + w->position * w->info_bytespersample, SEEK_SET); + return 0; + } +} diff --git a/wavefile.h b/wavefile.h new file mode 100644 index 00000000..de8b33aa --- /dev/null +++ b/wavefile.h @@ -0,0 +1,61 @@ + +#ifndef WAVEFILE_H +#define WAVEFILE_H + +typedef struct wavefile_s +{ + // file this is reading from + FILE *file; + + // these settings are read directly from the wave format + // 1 is uncompressed PCM + unsigned int info_format; + // how many samples per second + unsigned int info_rate; + // how many channels (1 = mono, 2 = stereo, 6 = 5.1 audio?) + unsigned int info_channels; + // how many bits per channel (8 or 16) + unsigned int info_bits; + + // these settings are generated from the wave format + // how many bytes in a sample (which may be one or two channels, thus 1 or 2 or 2 or 4, depending on info_bytesperchannel) + unsigned int info_bytespersample; + // how many bytes in channel (1 for 8bit, or 2 for 16bit) + unsigned int info_bytesperchannel; + + // how many samples in the wave file + unsigned int length; + + // how large the data chunk is + unsigned int datalength; + // beginning of data in data chunk + unsigned int dataposition; + + // current position in stream (in samples) + unsigned int position; + + // these are private to the wave file functions, just used for processing + // size of *buffer + unsigned int bufferlength; + // buffer is reallocated if caller asks for more than fits + void *buffer; + +} +wavefile_t; + +// opens a wave file, if an error occurs and errorstring is not NULL, +// *errorstring will be set to a message describing the error +wavefile_t *waveopen(char *filename, char **errorstring); +// closes a wave file +void waveclose(wavefile_t *f); + +// reads some data from the file as 16bit stereo (converting if necessary) +// returns number of samples read (may be less than requested) +// if not all samples could be read, the remaining buffer will be unmodified +unsigned int waveread16stereo(wavefile_t *f, short *soundbuffer, unsigned int samples); + +// seeks to a desired position in the wave +// returns 0 if successful, 1 if not successful +unsigned int waveseek(wavefile_t *f, unsigned int samples); + +#endif -- 2.39.2