Compare commits

...
16 Commits
Author SHA1 Message Date
OM 02df95fc21 Updated the README file 2023-05-18 20:54:12 +02:00
OM 03100b47c1 Set the project name to openmohaa 2023-05-18 20:24:09 +02:00
OM 67444b0f15 Fixed sound being muted on startup 2023-05-18 20:21:38 +02:00
OM f6ae9a2b43 Incremented the version number 2023-05-18 20:16:19 +02:00
OM 98cd85bc9a Fixed compilation under Linux 2023-05-18 20:07:54 +02:00
OM 83a83b08f6 Export PDB of app & modules 2023-05-18 19:56:46 +02:00
OM 448e9010dc Fixed alias random indexes being wrong 2023-05-18 19:47:50 +02:00
OM ce69ed75f7 Reverted to Quake 3 sound system 2023-05-18 19:47:37 +02:00
OM 6ac70d6b7c Fixed terrain patch not drawing properly 2023-05-18 18:51:45 +02:00
OM 4f588d939d Prevent spawning a tempmodel that has the world as a tempmodel 2023-05-18 18:45:25 +02:00
OM f6ebdd1998 Better multitexture / bundle support 2023-05-18 18:17:32 +02:00
OM 7438dd709c Fixed terrain unpacking 2023-05-18 18:17:18 +02:00
OM 1207b46937 Fixed terrains vertices being wrongly rendered 2023-05-18 17:15:52 +02:00
OM 497f853171 Fixed terrain varnodes being badly loaded 2023-05-18 17:14:00 +02:00
OM 4984f8b925 nextBundle fix 2023-05-18 16:40:17 +02:00
OM a1d581df39 Introducing full LOD terrain support 2023-05-18 14:41:37 +02:00
36 changed files with 3045 additions and 6247 deletions

No files matched your search

+6 -2
View File
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.5)
project(omohaaded)
project(openmohaa)
include(TargetArch.cmake)
@@ -205,7 +205,8 @@ if(WIN32)
endif()
target_link_libraries(omohaaded PRIVATE wsock32 ws2_32)
target_link_libraries(omohaaded PRIVATE winmm)
INSTALL(FILES $<TARGET_PDB_FILE:fgame> DESTINATION "${TARGET_BASE_GAME}" OPTIONAL)
INSTALL(FILES $<TARGET_PDB_FILE:omohaaded> DESTINATION "./" OPTIONAL)
elseif(UNIX)
find_package(Threads)
@@ -280,6 +281,9 @@ if (WITH_CLIENT)
target_link_libraries(openmohaa PRIVATE wsock32 ws2_32)
target_link_libraries(openmohaa PRIVATE winmm)
INSTALL(FILES $<TARGET_PDB_FILE:cgame> DESTINATION "${TARGET_BASE_GAME}" OPTIONAL)
INSTALL(FILES $<TARGET_PDB_FILE:openmohaa> DESTINATION "./" OPTIONAL)
elseif(UNIX)
find_package(OpenGL REQUIRED)
find_package(GLUT REQUIRED)
+23 -11
View File
@@ -14,28 +14,36 @@
## Current State
OpenMoHAA is still in its early days.
### Server
The server version can successfully be built. There are few unimplemented stuff from the game library :
The server version can successfully be built.
Some stuff are not working as intended, such as Actor or Vehicle. These systems are both almost fully implemented, but not really in stable state.
- Actor
- Vehicle
Overall the server part and the game module are almost fully implemented, but the stability may vary, there may be some minor bugs and crashes.
### Client
There is currently no client build because the UI code is currently unimplemented. Here is what's missing or unimplemented :
The client version of OpenMoHAA is partially implemented and not stable currently. The cgame module is partially implemented as well, but is compatible with the original MOH:AA game. Credits to **Heavy Metal: F.A.K.K. 2**'s SDK for the cgame module.
- rendering (mostly advanced)
- sound (must be reworked)
- UI
Here is the current working state for each part of the engine:
Using CMake, the client build and successfully be built by adding `-DWITH_CLIENT=1`
The client game module (cgame) is partially implemented, and is compatible with the original MOH:AA module.
| Part name | Full | Almost | Half | Early | Bad | Not working | Comment |
|-------------------------|------|--------|------|-------|-----|-------------|-------------------------------------------------------------------|
| Audio | | | | | x | | Very basic implementation from Quake III |
| CG Module | | x | | | | | Missing FX, Marks and decals |
| Client | | | x | | | | Missing GameSpy, server list, binds, map picker, ... |
| Collision | x | | | | | | |
| Model/TIKI/Skeletor | x | | | | | | |
| Renderer | | | | x | | | Missing ghost, marks, sphere lights, sky portal, sun flare, swipe |
| Server | | x | | | | | Probably a few bugs remaining |
| Server module (fgame) | | x | | | | | Actor, vehicle, and few gameplay bugs |
| UI | | | | x | | | Has core features only (menu, widgets, console, hud, urc loading) |
## Running
Backup the existing **gamex86.dll** (rename it to **gamex86.bak**).
Backup existing mohaa files, **cgamex86.dll** and **gamex86.dll** (set their extension to **.bak**)
a) extract archive to your MOHAA installation directory.
@@ -53,6 +61,8 @@ You can now start a local OpenMOHAA server or play on a server.
## Compiling
OpenMoHAA supports both x86 and x64 architectures, and although the target platform is Windows, it still can be compiled for other platforms, and even other architectures.
These are the tools required for all platforms :
- CMake >= 3.5
- Flex and Bison (currently not used)
@@ -60,6 +70,8 @@ These are the tools required for all platforms :
The installation directory can be set to MOHAA directory.
The client build and successfully be built by adding `-DWITH_CLIENT=1` to the CMake command line.
### Linux
These are the tools required on Unix :
+9
View File
@@ -4732,6 +4732,15 @@ void ClientGameCommandManager::SpawnTempModel(int mcount, int timeAlive)
}
}
}
// Make sure to not spawn the world model as a tempmodel
// Of course it would become extremely confusing...
if (!(p->cgd.flags & T_DLIGHT) && p->ent.reType == ET_MODELANIM_SKEL && !p->ent.hModel)
{
Com_Printf("^~^~^ not spawning tempmodel because it is using the world as a brush model\n");
FreeTempModel(p);
break;
}
}
}
+4 -4
View File
@@ -1142,17 +1142,17 @@ redump:
case ZA_SOUND_MONO:
if (!cinTable[currentHandle].silent) {
ssize = RllDecodeMonoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags);
S_RawSamples(0, ssize, 22050, 2, 1, (byte *)sbuf, 1.0f, -1);
S_RawSamples(0, 22050, 2, 1, (byte *)sbuf, 1.0f);
}
break;
case ZA_SOUND_STEREO:
if (!cinTable[currentHandle].silent) {
if (cinTable[currentHandle].numQuads == -1) {
S_Update();
s_rawend[0] = s_soundtime;
s_rawend = s_soundtime;
}
ssize = RllDecodeStereoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags);
S_RawSamples(0, ssize, 22050, 2, 2, (byte *)sbuf, 1.0f, -1);
S_RawSamples(0, 22050, 2, 2, (byte *)sbuf, 1.0f);
}
break;
case ROQ_QUAD_INFO:
@@ -1469,7 +1469,7 @@ int CIN_PlayCinematic( const char *arg, int x, int y, int w, int h, int systemBi
}
if (!cinTable[currentHandle].silent) {
s_rawend[0] = s_soundtime;
s_rawend = s_soundtime;
}
return currentHandle;
+4 -4
View File
@@ -25,7 +25,7 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/*
** Intel/DVI ADPCM coder/decoder.
**
** The algorithm for this coder was taken from the IMA Compatibility Project
** The algorithm for this coder was taken from the IMA Compatability Project
** proceedings, Vol 2, Number 2; May 1992.
**
** Version 1.2, 18-Dec-92.
@@ -278,7 +278,7 @@ void S_AdpcmGetSamples(sndBuffer *chunk, short *to) {
out = (byte *)chunk->sndChunk;
// get samples
S_AdpcmDecode((char *) out, to, SND_CHUNK_SIZE_BYTE*2, &state );
S_AdpcmDecode( out, to, SND_CHUNK_SIZE_BYTE*2, &state );
}
@@ -310,7 +310,7 @@ void S_AdpcmEncodeSound( sfx_t *sfx, short *samples ) {
newchunk = SND_malloc();
if (sfx->soundData == NULL) {
sfx->soundData = newchunk;
} else if (chunk != NULL) {
} else {
chunk->next = newchunk;
}
chunk = newchunk;
@@ -322,7 +322,7 @@ void S_AdpcmEncodeSound( sfx_t *sfx, short *samples ) {
out = (byte *)chunk->sndChunk;
// encode the samples
S_AdpcmEncode( samples + inOffset, (char *) out, n, &state );
S_AdpcmEncode( samples + inOffset, out, n, &state );
inOffset += n;
count -= n;
-229
View File
@@ -1,229 +0,0 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
/* This file is only compiled for PowerPC builds with Altivec support.
Altivec intrinsics need to be in a separate file, so GCC's -maltivec
command line can enable them, but give us the option to _not_ use that
on other files, where the compiler might then generate Altivec
instructions for normal floating point, crashing on G3 (etc) processors. */
#include "client.h"
#include "snd_local.h"
#if idppc_altivec
#if !defined(__APPLE__)
#include <altivec.h>
#endif
void S_PaintChannelFrom16_altivec( portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE], int snd_vol, channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
int data, aoff, boff;
int leftvol, rightvol;
int i, j;
portable_samplepair_t *samp;
sndBuffer *chunk;
short *samples;
float ooff, fdata[2], fdiv, fleftvol, frightvol;
if (sc->soundChannels <= 0) {
return;
}
samp = &paintbuffer[ bufferOffset ];
if (ch->doppler) {
sampleOffset = sampleOffset*ch->oldDopplerScale;
}
if ( sc->soundChannels == 2 ) {
sampleOffset *= sc->soundChannels;
if ( sampleOffset & 1 ) {
sampleOffset &= ~1;
}
}
chunk = sc->soundData;
while (sampleOffset>=SND_CHUNK_SIZE) {
chunk = chunk->next;
sampleOffset -= SND_CHUNK_SIZE;
if (!chunk) {
chunk = sc->soundData;
}
}
if (!ch->doppler || ch->dopplerScale==1.0f) {
vector signed short volume_vec;
vector unsigned int volume_shift;
int vectorCount, samplesLeft, chunkSamplesLeft;
leftvol = ch->leftvol*snd_vol;
rightvol = ch->rightvol*snd_vol;
samples = chunk->sndChunk;
((short *)&volume_vec)[0] = leftvol;
((short *)&volume_vec)[1] = leftvol;
((short *)&volume_vec)[4] = leftvol;
((short *)&volume_vec)[5] = leftvol;
((short *)&volume_vec)[2] = rightvol;
((short *)&volume_vec)[3] = rightvol;
((short *)&volume_vec)[6] = rightvol;
((short *)&volume_vec)[7] = rightvol;
volume_shift = vec_splat_u32(8);
i = 0;
while(i < count) {
/* Try to align destination to 16-byte boundary */
while(i < count && (((unsigned long)&samp[i] & 0x1f) || ((count-i) < 8) || ((SND_CHUNK_SIZE - sampleOffset) < 8))) {
data = samples[sampleOffset++];
samp[i].left += (data * leftvol)>>8;
if ( sc->soundChannels == 2 ) {
data = samples[sampleOffset++];
}
samp[i].right += (data * rightvol)>>8;
if (sampleOffset == SND_CHUNK_SIZE) {
chunk = chunk->next;
samples = chunk->sndChunk;
sampleOffset = 0;
}
i++;
}
/* Destination is now aligned. Process as many 8-sample
chunks as we can before we run out of room from the current
sound chunk. We do 8 per loop to avoid extra source data reads. */
samplesLeft = count - i;
chunkSamplesLeft = SND_CHUNK_SIZE - sampleOffset;
if(samplesLeft > chunkSamplesLeft)
samplesLeft = chunkSamplesLeft;
vectorCount = samplesLeft / 8;
if(vectorCount)
{
vector unsigned char tmp;
vector short s0, s1, sampleData0, sampleData1;
vector signed int merge0, merge1;
vector signed int d0, d1, d2, d3;
vector unsigned char samplePermute0 =
VECCONST_UINT8(0, 1, 4, 5, 0, 1, 4, 5, 2, 3, 6, 7, 2, 3, 6, 7);
vector unsigned char samplePermute1 =
VECCONST_UINT8(8, 9, 12, 13, 8, 9, 12, 13, 10, 11, 14, 15, 10, 11, 14, 15);
vector unsigned char loadPermute0, loadPermute1;
// Rather than permute the vectors after we load them to do the sample
// replication and rearrangement, we permute the alignment vector so
// we do everything in one step below and avoid data shuffling.
tmp = vec_lvsl(0,&samples[sampleOffset]);
loadPermute0 = vec_perm(tmp,tmp,samplePermute0);
loadPermute1 = vec_perm(tmp,tmp,samplePermute1);
s0 = *(vector short *)&samples[sampleOffset];
while(vectorCount)
{
/* Load up source (16-bit) sample data */
s1 = *(vector short *)&samples[sampleOffset+7];
/* Load up destination sample data */
d0 = *(vector signed int *)&samp[i];
d1 = *(vector signed int *)&samp[i+2];
d2 = *(vector signed int *)&samp[i+4];
d3 = *(vector signed int *)&samp[i+6];
sampleData0 = vec_perm(s0,s1,loadPermute0);
sampleData1 = vec_perm(s0,s1,loadPermute1);
merge0 = vec_mule(sampleData0,volume_vec);
merge0 = vec_sra(merge0,volume_shift); /* Shift down to proper range */
merge1 = vec_mulo(sampleData0,volume_vec);
merge1 = vec_sra(merge1,volume_shift);
d0 = vec_add(merge0,d0);
d1 = vec_add(merge1,d1);
merge0 = vec_mule(sampleData1,volume_vec);
merge0 = vec_sra(merge0,volume_shift); /* Shift down to proper range */
merge1 = vec_mulo(sampleData1,volume_vec);
merge1 = vec_sra(merge1,volume_shift);
d2 = vec_add(merge0,d2);
d3 = vec_add(merge1,d3);
/* Store destination sample data */
*(vector signed int *)&samp[i] = d0;
*(vector signed int *)&samp[i+2] = d1;
*(vector signed int *)&samp[i+4] = d2;
*(vector signed int *)&samp[i+6] = d3;
i += 8;
vectorCount--;
s0 = s1;
sampleOffset += 8;
}
if (sampleOffset == SND_CHUNK_SIZE) {
chunk = chunk->next;
samples = chunk->sndChunk;
sampleOffset = 0;
}
}
}
} else {
fleftvol = ch->leftvol*snd_vol;
frightvol = ch->rightvol*snd_vol;
ooff = sampleOffset;
samples = chunk->sndChunk;
for ( i=0 ; i<count ; i++ ) {
aoff = ooff;
ooff = ooff + ch->dopplerScale * sc->soundChannels;
boff = ooff;
fdata[0] = fdata[1] = 0;
for (j=aoff; j<boff; j += sc->soundChannels) {
if (j == SND_CHUNK_SIZE) {
chunk = chunk->next;
if (!chunk) {
chunk = sc->soundData;
}
samples = chunk->sndChunk;
ooff -= SND_CHUNK_SIZE;
}
if ( sc->soundChannels == 2 ) {
fdata[0] += samples[j&(SND_CHUNK_SIZE-1)];
fdata[1] += samples[(j+1)&(SND_CHUNK_SIZE-1)];
} else {
fdata[0] += samples[j&(SND_CHUNK_SIZE-1)];
fdata[1] += samples[j&(SND_CHUNK_SIZE-1)];
}
}
fdiv = 256 * (boff-aoff) / sc->soundChannels;
samp[i].left += (fdata[0] * fleftvol)/fdiv;
samp[i].right += (fdata[1] * frightvol)/fdiv;
}
}
}
#endif
-237
View File
@@ -1,237 +0,0 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "client.h"
#include "snd_codec.h"
static snd_codec_t *codecs;
/*
=================
S_CodecGetSound
Opens/loads a sound, tries codec based on the sound's file extension
then tries all supported codecs.
=================
*/
static void *S_CodecGetSound(const char *filename, snd_info_t *info)
{
snd_codec_t *codec;
snd_codec_t *orgCodec = NULL;
qboolean orgNameFailed = qfalse;
char localName[ MAX_QPATH ];
const char *ext;
char altName[ MAX_QPATH ];
void *rtn = NULL;
Q_strncpyz(localName, filename, MAX_QPATH);
ext = COM_GetExtension(localName);
if( *ext )
{
// Look for the correct loader and use it
for( codec = codecs; codec; codec = codec->next )
{
if( !Q_stricmp( ext, codec->ext ) )
{
// Load
if( info )
rtn = codec->load(localName, info);
else
rtn = codec->open(localName);
break;
}
}
// A loader was found
if( codec )
{
if( !rtn )
{
// Loader failed, most likely because the file isn't there;
// try again without the extension
orgNameFailed = qtrue;
orgCodec = codec;
COM_StripExtension( filename, localName, MAX_QPATH );
}
else
{
// Something loaded
return rtn;
}
}
}
// Try and find a suitable match using all
// the sound codecs supported
for( codec = codecs; codec; codec = codec->next )
{
if( codec == orgCodec )
continue;
Com_sprintf( altName, sizeof (altName), "%s.%s", localName, codec->ext );
// Load
if( info )
rtn = codec->load(altName, info);
else
rtn = codec->open(altName);
if( rtn )
{
if( orgNameFailed )
{
Com_DPrintf(S_COLOR_YELLOW "WARNING: %s not present, using %s instead\n",
filename, altName );
}
return rtn;
}
}
Com_Printf(S_COLOR_YELLOW "WARNING: Failed to %s sound %s!\n", info ? "load" : "open", filename);
return NULL;
}
/*
=================
S_CodecInit
=================
*/
void S_CodecInit()
{
codecs = NULL;
#ifdef USE_CODEC_OPUS
S_CodecRegister(&opus_codec);
#endif
#ifdef USE_CODEC_VORBIS
S_CodecRegister(&ogg_codec);
#endif
// Register wav codec last so that it is always tried first when a file extension was not found
S_CodecRegister(&wav_codec);
}
/*
=================
S_CodecShutdown
=================
*/
void S_CodecShutdown()
{
codecs = NULL;
}
/*
=================
S_CodecRegister
=================
*/
void S_CodecRegister(snd_codec_t *codec)
{
codec->next = codecs;
codecs = codec;
}
/*
=================
S_CodecLoad
=================
*/
void *S_CodecLoad(const char *filename, snd_info_t *info)
{
return S_CodecGetSound(filename, info);
}
/*
=================
S_CodecOpenStream
=================
*/
snd_stream_t *S_CodecOpenStream(const char *filename)
{
return S_CodecGetSound(filename, NULL);
}
void S_CodecCloseStream(snd_stream_t *stream)
{
stream->codec->close(stream);
}
int S_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer)
{
return stream->codec->read(stream, bytes, buffer);
}
//=======================================================================
// Util functions (used by codecs)
/*
=================
S_CodecUtilOpen
=================
*/
snd_stream_t *S_CodecUtilOpen(const char *filename, snd_codec_t *codec)
{
snd_stream_t *stream;
fileHandle_t hnd;
int length;
// Try to open the file
length = FS_FOpenFileRead(filename, &hnd, qtrue, qtrue);
if(!hnd)
{
Com_DPrintf("Can't read sound file %s\n", filename);
return NULL;
}
// Allocate a stream
stream = Z_Malloc(sizeof(snd_stream_t));
if(!stream)
{
FS_FCloseFile(hnd);
return NULL;
}
// Copy over, return
stream->codec = codec;
stream->file = hnd;
stream->length = length;
return stream;
}
/*
=================
S_CodecUtilClose
=================
*/
void S_CodecUtilClose(snd_stream_t **stream)
{
FS_FCloseFile((*stream)->file);
Z_Free(*stream);
*stream = NULL;
}
-107
View File
@@ -1,107 +0,0 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#ifndef _SND_CODEC_H_
#define _SND_CODEC_H_
#include "../qcommon/q_shared.h"
#include "../qcommon/qcommon.h"
typedef struct snd_info_s
{
int rate;
int width;
int channels;
int samples;
int size;
int dataofs;
} snd_info_t;
typedef struct snd_codec_s snd_codec_t;
typedef struct snd_stream_s
{
snd_codec_t *codec;
fileHandle_t file;
snd_info_t info;
int length;
int pos;
void *ptr;
} snd_stream_t;
// Codec functions
typedef void *(*CODEC_LOAD)(const char *filename, snd_info_t *info);
typedef snd_stream_t *(*CODEC_OPEN)(const char *filename);
typedef int (*CODEC_READ)(snd_stream_t *stream, int bytes, void *buffer);
typedef void (*CODEC_CLOSE)(snd_stream_t *stream);
// Codec data structure
struct snd_codec_s
{
char *ext;
CODEC_LOAD load;
CODEC_OPEN open;
CODEC_READ read;
CODEC_CLOSE close;
snd_codec_t *next;
};
// Codec management
void S_CodecInit( void );
void S_CodecShutdown( void );
void S_CodecRegister(snd_codec_t *codec);
void *S_CodecLoad(const char *filename, snd_info_t *info);
snd_stream_t *S_CodecOpenStream(const char *filename);
void S_CodecCloseStream(snd_stream_t *stream);
int S_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer);
// Util functions (used by codecs)
snd_stream_t *S_CodecUtilOpen(const char *filename, snd_codec_t *codec);
void S_CodecUtilClose(snd_stream_t **stream);
// WAV Codec
extern snd_codec_t wav_codec;
void *S_WAV_CodecLoad(const char *filename, snd_info_t *info);
snd_stream_t *S_WAV_CodecOpenStream(const char *filename);
void S_WAV_CodecCloseStream(snd_stream_t *stream);
int S_WAV_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer);
// Ogg Vorbis codec
#ifdef USE_CODEC_VORBIS
extern snd_codec_t ogg_codec;
void *S_OGG_CodecLoad(const char *filename, snd_info_t *info);
snd_stream_t *S_OGG_CodecOpenStream(const char *filename);
void S_OGG_CodecCloseStream(snd_stream_t *stream);
int S_OGG_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer);
#endif // USE_CODEC_VORBIS
// Ogg Opus codec
#ifdef USE_CODEC_OPUS
extern snd_codec_t opus_codec;
void *S_OggOpus_CodecLoad(const char *filename, snd_info_t *info);
snd_stream_t *S_OggOpus_CodecOpenStream(const char *filename);
void S_OggOpus_CodecCloseStream(snd_stream_t *stream);
int S_OggOpus_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer);
#endif // USE_CODEC_OPUS
#endif // !_SND_CODEC_H_
-717
View File
@@ -1,717 +0,0 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
Copyright (C) 2005-2006 Joerg Dietrich <dietrich_joerg@gmx.de>
Copyright (C) 2006 Thilo Schulz <arny@ats.s.bawue.de>
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#if USE_CODEC_MP3
// includes for the Q3 sound system
#include "client.h"
#include "snd_codec.h"
// includes for the MP3 codec
#include <mad.h>
#define MP3_SAMPLE_WIDTH 2
#define MP3_PCMSAMPLES_PERSLICE 32
// buffer size used when reading through the mp3
#define MP3_DATA_BUFSIZ 128*1024
// undefine this if you don't want any dithering.
#define MP3_DITHERING
// Q3 MP3 codec
snd_codec_t mp3_codec =
{
".mp3",
S_MP3_CodecLoad,
S_MP3_CodecOpenStream,
S_MP3_CodecReadStream,
S_MP3_CodecCloseStream,
NULL
};
// structure used for info purposes
struct snd_codec_mp3_info
{
byte encbuf[MP3_DATA_BUFSIZ]; // left over bytes not consumed
// by the decoder.
struct mad_stream madstream; // uses encbuf as buffer.
struct mad_frame madframe; // control structures for libmad.
struct mad_synth madsynth;
byte *pcmbuf; // buffer for not-used samples.
int buflen; // length of buffer data.
int pcmbufsize; // amount of allocated memory for
// pcmbuf. This should have at least
// the size of a decoded mp3 frame.
byte *dest; // copy decoded data here.
int destlen; // amount of already copied data.
int destsize; // amount of bytes we must decode.
};
/*************** MP3 utility functions ***************/
/*
=================
S_MP3_ReadData
=================
*/
// feed libmad with data
int S_MP3_ReadData(snd_stream_t *stream, struct mad_stream *madstream, byte *encbuf, int encbufsize)
{
int retval;
int leftover;
if(!stream)
return -1;
leftover = madstream->bufend - madstream->next_frame;
if(leftover > 0)
memmove(encbuf, madstream->this_frame, leftover);
// Fill the buffer right to the end
retval = FS_Read(&encbuf[leftover], encbufsize - leftover, stream->file);
if(retval <= 0)
{
// EOF reached, that's ok.
return 0;
}
mad_stream_buffer(madstream, encbuf, retval + leftover);
return retval;
}
/*
=================
S_MP3_Scanfile
to determine the samplecount, we apparently must get *all* headers :(
I basically used the xmms-mad plugin source to see how this stuff works.
returns a value < 0 on error.
=================
*/
int S_MP3_Scanfile(snd_stream_t *stream)
{
struct mad_stream madstream;
struct mad_header madheader;
int retval;
int samplecount;
byte encbuf[MP3_DATA_BUFSIZ];
// error out on invalid input.
if(!stream)
return -1;
mad_stream_init(&madstream);
mad_header_init(&madheader);
while(1)
{
retval = S_MP3_ReadData(stream, &madstream, encbuf, sizeof(encbuf));
if(retval < 0)
return -1;
else if(retval == 0)
break;
// Start decoding the headers.
while(1)
{
if((retval = mad_header_decode(&madheader, &madstream)) < 0)
{
if(madstream.error == MAD_ERROR_BUFLEN)
{
// We need to read more data
break;
}
if(!MAD_RECOVERABLE (madstream.error))
{
// unrecoverable error... we must bail out.
return retval;
}
mad_stream_skip(&madstream, madstream.skiplen);
continue;
}
// we got a valid header.
if(madheader.layer != MAD_LAYER_III)
{
// we don't support non-mp3s
return -1;
}
if(!stream->info.samples)
{
// This here is the very first frame. Set initial values now,
// that we expect to stay constant throughout the whole mp3.
stream->info.rate = madheader.samplerate;
stream->info.width = MP3_SAMPLE_WIDTH;
stream->info.channels = MAD_NCHANNELS(&madheader);
stream->info.samples = 0;
stream->info.size = 0; // same here.
stream->info.dataofs = 0;
}
else
{
// Check whether something changed that shouldn't.
if(stream->info.rate != madheader.samplerate ||
stream->info.channels != MAD_NCHANNELS(&madheader))
return -1;
}
// Update the counters
samplecount = MAD_NSBSAMPLES(&madheader) * MP3_PCMSAMPLES_PERSLICE;
stream->info.samples += samplecount;
stream->info.size += samplecount * stream->info.channels * stream->info.width;
}
}
// Reset the file pointer so we can do the real decoding.
FS_Seek(stream->file, 0, FS_SEEK_SET);
return 0;
}
/************************ dithering functions ***************************/
#ifdef MP3_DITHERING
// All dithering done here is taken from the GPL'ed xmms-mad plugin.
/* Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura. */
/* Any feedback is very welcome. For any question, comments, */
/* see http://www.math.keio.ac.jp/matumoto/emt.html or email */
/* matumoto@math.keio.ac.jp */
/* Period parameters */
#define MP3_DITH_N 624
#define MP3_DITH_M 397
#define MATRIX_A 0x9908b0df /* constant vector a */
#define UPPER_MASK 0x80000000 /* most significant w-r bits */
#define LOWER_MASK 0x7fffffff /* least significant r bits */
/* Tempering parameters */
#define TEMPERING_MASK_B 0x9d2c5680
#define TEMPERING_MASK_C 0xefc60000
#define TEMPERING_SHIFT_U(y) (y >> 11)
#define TEMPERING_SHIFT_S(y) (y << 7)
#define TEMPERING_SHIFT_T(y) (y << 15)
#define TEMPERING_SHIFT_L(y) (y >> 18)
static unsigned long mt[MP3_DITH_N]; /* the array for the state vector */
static int mti=MP3_DITH_N+1; /* mti==MP3_DITH_N+1 means mt[MP3_DITH_N] is not initialized */
/* initializing the array with a NONZERO seed */
void sgenrand(unsigned long seed)
{
/* setting initial seeds to mt[MP3_DITH_N] using */
/* the generator Line 25 of Table 1 in */
/* [KNUTH 1981, The Art of Computer Programming */
/* Vol. 2 (2nd Ed.), pp102] */
mt[0]= seed & 0xffffffff;
for (mti=1; mti<MP3_DITH_N; mti++)
mt[mti] = (69069 * mt[mti-1]) & 0xffffffff;
}
unsigned long genrand(void)
{
unsigned long y;
static unsigned long mag01[2]={0x0, MATRIX_A};
/* mag01[x] = x * MATRIX_A for x=0,1 */
if (mti >= MP3_DITH_N) { /* generate MP3_DITH_N words at one time */
int kk;
if (mti == MP3_DITH_N+1) /* if sgenrand() has not been called, */
sgenrand(4357); /* a default initial seed is used */
for (kk=0;kk<MP3_DITH_N-MP3_DITH_M;kk++) {
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
mt[kk] = mt[kk+MP3_DITH_M] ^ (y >> 1) ^ mag01[y & 0x1];
}
for (;kk<MP3_DITH_N-1;kk++) {
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
mt[kk] = mt[kk+(MP3_DITH_M-MP3_DITH_N)] ^ (y >> 1) ^ mag01[y & 0x1];
}
y = (mt[MP3_DITH_N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
mt[MP3_DITH_N-1] = mt[MP3_DITH_M-1] ^ (y >> 1) ^ mag01[y & 0x1];
mti = 0;
}
y = mt[mti++];
y ^= TEMPERING_SHIFT_U(y);
y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B;
y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C;
y ^= TEMPERING_SHIFT_L(y);
return y;
}
long triangular_dither_noise(int nbits) {
// parameter nbits : the peak-to-peak amplitude desired (in bits)
// use with nbits set to 2 + nber of bits to be trimmed.
// (because triangular is made from two uniformly distributed processes,
// it starts at 2 bits peak-to-peak amplitude)
// see The Theory of Dithered Quantization by Robert Alexander Wannamaker
// for complete proof of why that's optimal
long v = (genrand()/2 - genrand()/2); // in ]-2^31, 2^31[
//int signe = (v>0) ? 1 : -1;
long P = 1 << (32 - nbits); // the power of 2
v /= P;
// now v in ]-2^(nbits-1), 2^(nbits-1) [
return v;
}
#endif // MP3_DITHERING
/************************ decoder functions ***************************/
/*
=================
S_MP3_Scale
Converts the signal to 16 bit LE-PCM data and does dithering.
- borrowed from xmms-mad plugin source.
=================
*/
/*
* xmms-mad - mp3 plugin for xmms
* Copyright (C) 2001-2002 Sam Clegg
*/
signed int S_MP3_Scale(mad_fixed_t sample)
{
int n_bits_to_loose = MAD_F_FRACBITS + 1 - 16;
#ifdef MP3_DITHERING
int dither;
#endif
// round
sample += (1L << (n_bits_to_loose - 1));
#ifdef MP3_DITHERING
dither = triangular_dither_noise(n_bits_to_loose + 1);
sample += dither;
#endif
/* clip */
if (sample >= MAD_F_ONE)
sample = MAD_F_ONE - 1;
else if (sample < -MAD_F_ONE)
sample = -MAD_F_ONE;
/* quantize */
return sample >> n_bits_to_loose;
}
/*
=================
S_MP3_PCMCopy
Copy and convert pcm data until bytecount bytes have been written.
return the position in pcm->samples.
indicate the amount of actually written bytes in wrotecnt.
=================
*/
int S_MP3_PCMCopy(byte *buf, struct mad_pcm *pcm, int bufofs,
int sampleofs, int bytecount, int *wrotecnt)
{
int written = 0;
signed int sample;
int framesize = pcm->channels * MP3_SAMPLE_WIDTH;
// add new pcm data.
while(written < bytecount && sampleofs < pcm->length)
{
sample = S_MP3_Scale(pcm->samples[0][sampleofs]);
#ifdef Q3_BIG_ENDIAN
// output to 16 bit big endian PCM
buf[bufofs++] = (sample >> 8) & 0xff;
buf[bufofs++] = sample & 0xff;
#else
// output to 16 bit little endian PCM
buf[bufofs++] = sample & 0xff;
buf[bufofs++] = (sample >> 8) & 0xff;
#endif
if(pcm->channels == 2)
{
sample = S_MP3_Scale(pcm->samples[1][sampleofs]);
#ifdef Q3_BIG_ENDIAN
buf[bufofs++] = (sample >> 8) & 0xff;
buf[bufofs++] = sample & 0xff;
#else
buf[bufofs++] = sample & 0xff;
buf[bufofs++] = (sample >> 8) & 0xff;
#endif
}
sampleofs++;
written += framesize;
}
if(wrotecnt)
*wrotecnt = written;
return sampleofs;
}
/*
=================
S_MP3_Decode
=================
*/
// gets executed for every decoded frame.
int S_MP3_Decode(snd_stream_t *stream)
{
struct snd_codec_mp3_info *mp3info;
struct mad_stream *madstream;
struct mad_frame *madframe;
struct mad_synth *madsynth;
struct mad_pcm *pcm;
int cursize;
int samplecount;
int needcount;
int wrote;
int retval;
if(!stream)
return -1;
mp3info = stream->ptr;
madstream = &mp3info->madstream;
madframe = &mp3info->madframe;
if(mad_frame_decode(madframe, madstream))
{
if(madstream->error == MAD_ERROR_BUFLEN)
{
// we need more data. Read another chunk.
retval = S_MP3_ReadData(stream, madstream, mp3info->encbuf, sizeof(mp3info->encbuf));
// call myself again now that buffer is full.
if(retval > 0)
retval = S_MP3_Decode(stream);
}
else if(MAD_RECOVERABLE(madstream->error))
{
mad_stream_skip(madstream, madstream->skiplen);
return S_MP3_Decode(stream);
}
else
retval = -1;
return retval;
}
// check whether this really is an mp3
if(madframe->header.layer != MAD_LAYER_III)
return -1;
// generate pcm data
madsynth = &mp3info->madsynth;
mad_synth_frame(madsynth, madframe);
pcm = &madsynth->pcm;
// perform a few checks to see whether something changed that shouldn't.
if(stream->info.rate != pcm->samplerate ||
stream->info.channels != pcm->channels)
{
return -1;
}
// see whether we have got enough data now.
cursize = pcm->length * pcm->channels * stream->info.width;
needcount = mp3info->destsize - mp3info->destlen;
// Copy exactly as many samples as required.
samplecount = S_MP3_PCMCopy(mp3info->dest, pcm,
mp3info->destlen, 0, needcount, &wrote);
mp3info->destlen += wrote;
if(samplecount < pcm->length)
{
// Not all samples got copied. Copy the rest into the pcm buffer.
samplecount = S_MP3_PCMCopy(mp3info->pcmbuf, pcm,
mp3info->buflen,
samplecount,
mp3info->pcmbufsize - mp3info->buflen,
&wrote);
mp3info->buflen += wrote;
if(samplecount < pcm->length)
{
// The pcm buffer was not large enough. Make it bigger.
byte *newbuf = Z_Malloc(cursize);
if(mp3info->pcmbuf)
{
memcpy(newbuf, mp3info->pcmbuf, mp3info->buflen);
Z_Free(mp3info->pcmbuf);
}
mp3info->pcmbuf = newbuf;
mp3info->pcmbufsize = cursize;
samplecount = S_MP3_PCMCopy(mp3info->pcmbuf, pcm,
mp3info->buflen,
samplecount,
mp3info->pcmbufsize - mp3info->buflen,
&wrote);
mp3info->buflen += wrote;
}
// we're definitely done.
retval = 0;
}
else if(mp3info->destlen >= mp3info->destsize)
retval = 0;
else
retval = 1;
return retval;
}
/*************** Callback functions for quake3 ***************/
/*
=================
S_MP3_CodecOpenStream
=================
*/
snd_stream_t *S_MP3_CodecOpenStream(const char *filename)
{
snd_stream_t *stream;
struct snd_codec_mp3_info *mp3info;
// Open the stream
stream = S_CodecUtilOpen(filename, &mp3_codec);
if(!stream || stream->length <= 0)
return NULL;
// We have to scan through the MP3 to determine the important mp3 info.
if(S_MP3_Scanfile(stream) < 0)
{
// scanning didn't work out...
S_CodecUtilClose(&stream);
return NULL;
}
// Initialize the mp3 info structure we need for streaming
mp3info = Z_Malloc(sizeof(*mp3info));
if(!mp3info)
{
S_CodecUtilClose(&stream);
return NULL;
}
stream->ptr = mp3info;
// initialize the libmad control structures.
mad_stream_init(&mp3info->madstream);
mad_frame_init(&mp3info->madframe);
mad_synth_init(&mp3info->madsynth);
if(S_MP3_ReadData(stream, &mp3info->madstream, mp3info->encbuf, sizeof(mp3info->encbuf)) <= 0)
{
// we didnt read anything, that's bad.
S_MP3_CodecCloseStream(stream);
return NULL;
}
return stream;
}
/*
=================
S_MP3_CodecCloseStream
=================
*/
// free all memory we allocated.
void S_MP3_CodecCloseStream(snd_stream_t *stream)
{
struct snd_codec_mp3_info *mp3info;
if(!stream)
return;
// free all data in our mp3info tree
if(stream->ptr)
{
mp3info = stream->ptr;
if(mp3info->pcmbuf)
Z_Free(mp3info->pcmbuf);
mad_synth_finish(&mp3info->madsynth);
mad_frame_finish(&mp3info->madframe);
mad_stream_finish(&mp3info->madstream);
Z_Free(stream->ptr);
}
S_CodecUtilClose(&stream);
}
/*
=================
S_MP3_CodecReadStream
=================
*/
int S_MP3_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer)
{
struct snd_codec_mp3_info *mp3info;
int retval;
if(!stream)
return -1;
mp3info = stream->ptr;
// Make sure we get complete frames all the way through.
bytes -= bytes % (stream->info.channels * stream->info.width);
if(mp3info->buflen)
{
if(bytes < mp3info->buflen)
{
// we still have enough bytes in our decoded pcm buffer
memcpy(buffer, mp3info->pcmbuf, bytes);
// remove the portion from our buffer.
mp3info->buflen -= bytes;
memmove(mp3info->pcmbuf, &mp3info->pcmbuf[bytes], mp3info->buflen);
return bytes;
}
else
{
// copy over the samples we already have.
memcpy(buffer, mp3info->pcmbuf, mp3info->buflen);
mp3info->destlen = mp3info->buflen;
mp3info->buflen = 0;
}
}
else
mp3info->destlen = 0;
mp3info->dest = buffer;
mp3info->destsize = bytes;
do
{
retval = S_MP3_Decode(stream);
} while(retval > 0);
// if there was an error return nothing.
if(retval < 0)
return 0;
return mp3info->destlen;
}
/*
=====================================================================
S_MP3_CodecLoad
We handle S_MP3_CodecLoad as a special case of the streaming functions
where we read the whole stream at once.
======================================================================
*/
void *S_MP3_CodecLoad(const char *filename, snd_info_t *info)
{
snd_stream_t *stream;
byte *pcmbuffer;
// check if input is valid
if(!filename)
return NULL;
stream = S_MP3_CodecOpenStream(filename);
if(!stream)
return NULL;
// copy over the info
info->rate = stream->info.rate;
info->width = stream->info.width;
info->channels = stream->info.channels;
info->samples = stream->info.samples;
info->dataofs = stream->info.dataofs;
// allocate enough buffer for all pcm data
// pcmbuffer = Z_Malloc(stream->info.size);
pcmbuffer = Hunk_AllocateTempMemory(stream->info.size);
if(!pcmbuffer)
{
S_MP3_CodecCloseStream(stream);
return NULL;
}
info->size = S_MP3_CodecReadStream(stream, stream->info.size, pcmbuffer);
if(info->size <= 0)
{
// we didn't read anything at all. darn.
// Z_Free(pcmbuffer);
Hunk_FreeTempMemory(pcmbuffer);
pcmbuffer = NULL;
}
S_MP3_CodecCloseStream(stream);
return pcmbuffer;
}
#endif
-475
View File
@@ -1,475 +0,0 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
Copyright (C) 2005-2006 Joerg Dietrich <dietrich_joerg@gmx.de>
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// OGG support is enabled by this define
#ifdef USE_CODEC_VORBIS
// includes for the Q3 sound system
#include "client.h"
#include "snd_codec.h"
// includes for the OGG codec
#include <errno.h>
#define OV_EXCLUDE_STATIC_CALLBACKS
#include <vorbis/vorbisfile.h>
// The OGG codec can return the samples in a number of different formats,
// we use the standard signed short format.
#define OGG_SAMPLEWIDTH 2
// Q3 OGG codec
snd_codec_t ogg_codec =
{
"ogg",
S_OGG_CodecLoad,
S_OGG_CodecOpenStream,
S_OGG_CodecReadStream,
S_OGG_CodecCloseStream,
NULL
};
// callbacks for vobisfile
// fread() replacement
size_t S_OGG_Callback_read(void *ptr, size_t size, size_t nmemb, void *datasource)
{
snd_stream_t *stream;
int byteSize = 0;
int bytesRead = 0;
size_t nMembRead = 0;
// check if input is valid
if(!ptr)
{
errno = EFAULT;
return 0;
}
if(!(size && nmemb))
{
// It's not an error, caller just wants zero bytes!
errno = 0;
return 0;
}
if(!datasource)
{
errno = EBADF;
return 0;
}
// we use a snd_stream_t in the generic pointer to pass around
stream = (snd_stream_t *) datasource;
// FS_Read does not support multi-byte elements
byteSize = nmemb * size;
// read it with the Q3 function FS_Read()
bytesRead = FS_Read(ptr, byteSize, stream->file);
// update the file position
stream->pos += bytesRead;
// this function returns the number of elements read not the number of bytes
nMembRead = bytesRead / size;
// even if the last member is only read partially
// it is counted as a whole in the return value
if(bytesRead % size)
{
nMembRead++;
}
return nMembRead;
}
// fseek() replacement
int S_OGG_Callback_seek(void *datasource, ogg_int64_t offset, int whence)
{
snd_stream_t *stream;
int retVal = 0;
// check if input is valid
if(!datasource)
{
errno = EBADF;
return -1;
}
// snd_stream_t in the generic pointer
stream = (snd_stream_t *) datasource;
// we must map the whence to its Q3 counterpart
switch(whence)
{
case SEEK_SET :
{
// set the file position in the actual file with the Q3 function
retVal = FS_Seek(stream->file, (long) offset, FS_SEEK_SET);
// something has gone wrong, so we return here
if(retVal < 0)
{
return retVal;
}
// keep track of file position
stream->pos = (int) offset;
break;
}
case SEEK_CUR :
{
// set the file position in the actual file with the Q3 function
retVal = FS_Seek(stream->file, (long) offset, FS_SEEK_CUR);
// something has gone wrong, so we return here
if(retVal < 0)
{
return retVal;
}
// keep track of file position
stream->pos += (int) offset;
break;
}
case SEEK_END :
{
// set the file position in the actual file with the Q3 function
retVal = FS_Seek(stream->file, (long) offset, FS_SEEK_END);
// something has gone wrong, so we return here
if(retVal < 0)
{
return retVal;
}
// keep track of file position
stream->pos = stream->length + (int) offset;
break;
}
default :
{
// unknown whence, so we return an error
errno = EINVAL;
return -1;
}
}
// stream->pos shouldn't be smaller than zero or bigger than the filesize
stream->pos = (stream->pos < 0) ? 0 : stream->pos;
stream->pos = (stream->pos > stream->length) ? stream->length : stream->pos;
return 0;
}
// fclose() replacement
int S_OGG_Callback_close(void *datasource)
{
// we do nothing here and close all things manually in S_OGG_CodecCloseStream()
return 0;
}
// ftell() replacement
long S_OGG_Callback_tell(void *datasource)
{
snd_stream_t *stream;
// check if input is valid
if(!datasource)
{
errno = EBADF;
return -1;
}
// snd_stream_t in the generic pointer
stream = (snd_stream_t *) datasource;
return (long) FS_FTell(stream->file);
}
// the callback structure
const ov_callbacks S_OGG_Callbacks =
{
&S_OGG_Callback_read,
&S_OGG_Callback_seek,
&S_OGG_Callback_close,
&S_OGG_Callback_tell
};
/*
=================
S_OGG_CodecOpenStream
=================
*/
snd_stream_t *S_OGG_CodecOpenStream(const char *filename)
{
snd_stream_t *stream;
// OGG codec control structure
OggVorbis_File *vf;
// some variables used to get informations about the OGG
vorbis_info *OGGInfo;
ogg_int64_t numSamples;
// check if input is valid
if(!filename)
{
return NULL;
}
// Open the stream
stream = S_CodecUtilOpen(filename, &ogg_codec);
if(!stream)
{
return NULL;
}
// alloctate the OggVorbis_File
vf = Z_Malloc(sizeof(OggVorbis_File));
if(!vf)
{
S_CodecUtilClose(&stream);
return NULL;
}
// open the codec with our callbacks and stream as the generic pointer
if(ov_open_callbacks(stream, vf, NULL, 0, S_OGG_Callbacks) != 0)
{
Z_Free(vf);
S_CodecUtilClose(&stream);
return NULL;
}
// the stream must be seekable
if(!ov_seekable(vf))
{
ov_clear(vf);
Z_Free(vf);
S_CodecUtilClose(&stream);
return NULL;
}
// we only support OGGs with one substream
if(ov_streams(vf) != 1)
{
ov_clear(vf);
Z_Free(vf);
S_CodecUtilClose(&stream);
return NULL;
}
// get the info about channels and rate
OGGInfo = ov_info(vf, 0);
if(!OGGInfo)
{
ov_clear(vf);
Z_Free(vf);
S_CodecUtilClose(&stream);
return NULL;
}
// get the number of sample-frames in the OGG
numSamples = ov_pcm_total(vf, 0);
// fill in the info-structure in the stream
stream->info.rate = OGGInfo->rate;
stream->info.width = OGG_SAMPLEWIDTH;
stream->info.channels = OGGInfo->channels;
stream->info.samples = numSamples;
stream->info.size = stream->info.samples * stream->info.channels * stream->info.width;
stream->info.dataofs = 0;
// We use stream->pos for the file pointer in the compressed ogg file
stream->pos = 0;
// We use the generic pointer in stream for the OGG codec control structure
stream->ptr = vf;
return stream;
}
/*
=================
S_OGG_CodecCloseStream
=================
*/
void S_OGG_CodecCloseStream(snd_stream_t *stream)
{
// check if input is valid
if(!stream)
{
return;
}
// let the OGG codec cleanup its stuff
ov_clear((OggVorbis_File *) stream->ptr);
// free the OGG codec control struct
Z_Free(stream->ptr);
// close the stream
S_CodecUtilClose(&stream);
}
/*
=================
S_OGG_CodecReadStream
=================
*/
int S_OGG_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer)
{
// buffer handling
int bytesRead, bytesLeft, c;
char *bufPtr;
// Bitstream for the decoder
int BS = 0;
// big endian machines want their samples in big endian order
int IsBigEndian = 0;
# ifdef Q3_BIG_ENDIAN
IsBigEndian = 1;
# endif // Q3_BIG_ENDIAN
// check if input is valid
if(!(stream && buffer))
{
return 0;
}
if(bytes <= 0)
{
return 0;
}
bytesRead = 0;
bytesLeft = bytes;
bufPtr = buffer;
// cycle until we have the requested or all available bytes read
while(-1)
{
// read some bytes from the OGG codec
c = ov_read((OggVorbis_File *) stream->ptr, bufPtr, bytesLeft, IsBigEndian, OGG_SAMPLEWIDTH, 1, &BS);
// no more bytes are left
if(c <= 0)
{
break;
}
bytesRead += c;
bytesLeft -= c;
bufPtr += c;
// we have enough bytes
if(bytesLeft <= 0)
{
break;
}
}
return bytesRead;
}
/*
=====================================================================
S_OGG_CodecLoad
We handle S_OGG_CodecLoad as a special case of the streaming functions
where we read the whole stream at once.
======================================================================
*/
void *S_OGG_CodecLoad(const char *filename, snd_info_t *info)
{
snd_stream_t *stream;
byte *buffer;
int bytesRead;
// check if input is valid
if(!(filename && info))
{
return NULL;
}
// open the file as a stream
stream = S_OGG_CodecOpenStream(filename);
if(!stream)
{
return NULL;
}
// copy over the info
info->rate = stream->info.rate;
info->width = stream->info.width;
info->channels = stream->info.channels;
info->samples = stream->info.samples;
info->size = stream->info.size;
info->dataofs = stream->info.dataofs;
// allocate a buffer
// this buffer must be free-ed by the caller of this function
buffer = Hunk_AllocateTempMemory(info->size);
if(!buffer)
{
S_OGG_CodecCloseStream(stream);
return NULL;
}
// fill the buffer
bytesRead = S_OGG_CodecReadStream(stream, info->size, buffer);
// we don't even have read a single byte
if(bytesRead <= 0)
{
Hunk_FreeTempMemory(buffer);
S_OGG_CodecCloseStream(stream);
return NULL;
}
S_OGG_CodecCloseStream(stream);
return buffer;
}
#endif // USE_CODEC_VORBIS
-449
View File
@@ -1,449 +0,0 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
Copyright (C) 2005-2006 Joerg Dietrich <dietrich_joerg@gmx.de>
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// Ogg Opus support is enabled by this define
#ifdef USE_CODEC_OPUS
// includes for the Q3 sound system
#include "client.h"
#include "snd_codec.h"
// includes for the Ogg Opus codec
#include <errno.h>
#include <opusfile.h>
// samples are 16 bit
#define OPUS_SAMPLEWIDTH 2
// Q3 Ogg Opus codec
snd_codec_t opus_codec =
{
"opus",
S_OggOpus_CodecLoad,
S_OggOpus_CodecOpenStream,
S_OggOpus_CodecReadStream,
S_OggOpus_CodecCloseStream,
NULL
};
// callbacks for opusfile
// fread() replacement
int S_OggOpus_Callback_read(void *datasource, unsigned char *ptr, int size )
{
snd_stream_t *stream;
int bytesRead = 0;
// check if input is valid
if(!ptr)
{
errno = EFAULT;
return -1;
}
if(!size)
{
// It's not an error, caller just wants zero bytes!
errno = 0;
return 0;
}
if (size < 0)
{
errno = EINVAL;
return -1;
}
if(!datasource)
{
errno = EBADF;
return -1;
}
// we use a snd_stream_t in the generic pointer to pass around
stream = (snd_stream_t *) datasource;
// read it with the Q3 function FS_Read()
bytesRead = FS_Read(ptr, size, stream->file);
// update the file position
stream->pos += bytesRead;
return bytesRead;
}
// fseek() replacement
int S_OggOpus_Callback_seek(void *datasource, opus_int64 offset, int whence)
{
snd_stream_t *stream;
int retVal = 0;
// check if input is valid
if(!datasource)
{
errno = EBADF;
return -1;
}
// snd_stream_t in the generic pointer
stream = (snd_stream_t *) datasource;
// we must map the whence to its Q3 counterpart
switch(whence)
{
case SEEK_SET :
{
// set the file position in the actual file with the Q3 function
retVal = FS_Seek(stream->file, (long) offset, FS_SEEK_SET);
// something has gone wrong, so we return here
if(retVal < 0)
{
return retVal;
}
// keep track of file position
stream->pos = (int) offset;
break;
}
case SEEK_CUR :
{
// set the file position in the actual file with the Q3 function
retVal = FS_Seek(stream->file, (long) offset, FS_SEEK_CUR);
// something has gone wrong, so we return here
if(retVal < 0)
{
return retVal;
}
// keep track of file position
stream->pos += (int) offset;
break;
}
case SEEK_END :
{
// set the file position in the actual file with the Q3 function
retVal = FS_Seek(stream->file, (long) offset, FS_SEEK_END);
// something has gone wrong, so we return here
if(retVal < 0)
{
return retVal;
}
// keep track of file position
stream->pos = stream->length + (int) offset;
break;
}
default :
{
// unknown whence, so we return an error
errno = EINVAL;
return -1;
}
}
// stream->pos shouldn't be smaller than zero or bigger than the filesize
stream->pos = (stream->pos < 0) ? 0 : stream->pos;
stream->pos = (stream->pos > stream->length) ? stream->length : stream->pos;
return 0;
}
// fclose() replacement
int S_OggOpus_Callback_close(void *datasource)
{
// we do nothing here and close all things manually in S_OggOpus_CodecCloseStream()
return 0;
}
// ftell() replacement
opus_int64 S_OggOpus_Callback_tell(void *datasource)
{
snd_stream_t *stream;
// check if input is valid
if(!datasource)
{
errno = EBADF;
return -1;
}
// snd_stream_t in the generic pointer
stream = (snd_stream_t *) datasource;
return (opus_int64) FS_FTell(stream->file);
}
// the callback structure
const OpusFileCallbacks S_OggOpus_Callbacks =
{
&S_OggOpus_Callback_read,
&S_OggOpus_Callback_seek,
&S_OggOpus_Callback_tell,
&S_OggOpus_Callback_close
};
/*
=================
S_OggOpus_CodecOpenStream
=================
*/
snd_stream_t *S_OggOpus_CodecOpenStream(const char *filename)
{
snd_stream_t *stream;
// Opus codec control structure
OggOpusFile *of;
// some variables used to get informations about the file
const OpusHead *opusInfo;
ogg_int64_t numSamples;
// check if input is valid
if(!filename)
{
return NULL;
}
// Open the stream
stream = S_CodecUtilOpen(filename, &opus_codec);
if(!stream)
{
return NULL;
}
// open the codec with our callbacks and stream as the generic pointer
of = op_open_callbacks(stream, &S_OggOpus_Callbacks, NULL, 0, NULL );
if (!of)
{
S_CodecUtilClose(&stream);
return NULL;
}
// the stream must be seekable
if(!op_seekable(of))
{
op_free(of);
S_CodecUtilClose(&stream);
return NULL;
}
// get the info about channels and rate
opusInfo = op_head(of, -1);
if(!opusInfo)
{
op_free(of);
S_CodecUtilClose(&stream);
return NULL;
}
if(opusInfo->stream_count != 1)
{
op_free(of);
S_CodecUtilClose(&stream);
Com_Printf("Only Ogg Opus files with one stream are support\n");
return NULL;
}
if(opusInfo->channel_count != 1 && opusInfo->channel_count != 2)
{
op_free(of);
S_CodecUtilClose(&stream);
Com_Printf("Only mono and stereo Ogg Opus files are supported\n");
return NULL;
}
// get the number of sample-frames in the file
numSamples = op_pcm_total(of, -1);
// fill in the info-structure in the stream
stream->info.rate = 48000;
stream->info.width = OPUS_SAMPLEWIDTH;
stream->info.channels = opusInfo->channel_count;
stream->info.samples = numSamples;
stream->info.size = stream->info.samples * stream->info.channels * stream->info.width;
stream->info.dataofs = 0;
// We use stream->pos for the file pointer in the compressed ogg file
stream->pos = 0;
// We use the generic pointer in stream for the opus codec control structure
stream->ptr = of;
return stream;
}
/*
=================
S_OggOpus_CodecCloseStream
=================
*/
void S_OggOpus_CodecCloseStream(snd_stream_t *stream)
{
// check if input is valid
if(!stream)
{
return;
}
// let the opus codec cleanup its stuff
op_free((OggOpusFile *) stream->ptr);
// close the stream
S_CodecUtilClose(&stream);
}
/*
=================
S_OggOpus_CodecReadStream
=================
*/
int S_OggOpus_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer)
{
// buffer handling
int samplesRead, samplesLeft, c;
opus_int16 *bufPtr;
// check if input is valid
if(!(stream && buffer))
{
return 0;
}
if(bytes <= 0)
{
return 0;
}
samplesRead = 0;
samplesLeft = bytes / stream->info.channels / stream->info.width;
bufPtr = buffer;
if(samplesLeft <= 0)
{
return 0;
}
// cycle until we have the requested or all available bytes read
while(-1)
{
// read some samples from the opus codec
c = op_read((OggOpusFile *) stream->ptr, bufPtr + samplesRead * stream->info.channels, samplesLeft * stream->info.channels, NULL);
// no more samples are left
if(c <= 0)
{
break;
}
samplesRead += c;
samplesLeft -= c;
// we have enough samples
if(samplesLeft <= 0)
{
break;
}
}
return samplesRead * stream->info.channels * stream->info.width;
}
/*
=====================================================================
S_OggOpus_CodecLoad
We handle S_OggOpus_CodecLoad as a special case of the streaming functions
where we read the whole stream at once.
======================================================================
*/
void *S_OggOpus_CodecLoad(const char *filename, snd_info_t *info)
{
snd_stream_t *stream;
byte *buffer;
int bytesRead;
// check if input is valid
if(!(filename && info))
{
return NULL;
}
// open the file as a stream
stream = S_OggOpus_CodecOpenStream(filename);
if(!stream)
{
return NULL;
}
// copy over the info
info->rate = stream->info.rate;
info->width = stream->info.width;
info->channels = stream->info.channels;
info->samples = stream->info.samples;
info->size = stream->info.size;
info->dataofs = stream->info.dataofs;
// allocate a buffer
// this buffer must be free-ed by the caller of this function
buffer = Hunk_AllocateTempMemory(info->size);
if(!buffer)
{
S_OggOpus_CodecCloseStream(stream);
return NULL;
}
// fill the buffer
bytesRead = S_OggOpus_CodecReadStream(stream, info->size, buffer);
// we don't even have read a single byte
if(bytesRead <= 0)
{
Hunk_FreeTempMemory(buffer);
S_OggOpus_CodecCloseStream(stream);
return NULL;
}
S_OggOpus_CodecCloseStream(stream);
return buffer;
}
#endif // USE_CODEC_OPUS
-291
View File
@@ -1,291 +0,0 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "client.h"
#include "snd_codec.h"
/*
=================
FGetLittleLong
=================
*/
static int FGetLittleLong( fileHandle_t f ) {
int v;
FS_Read( &v, sizeof(v), f );
return LittleLong( v);
}
/*
=================
FGetLittleShort
=================
*/
static short FGetLittleShort( fileHandle_t f ) {
short v;
FS_Read( &v, sizeof(v), f );
return LittleShort( v);
}
/*
=================
S_ReadChunkInfo
=================
*/
static int S_ReadChunkInfo(fileHandle_t f, char *name)
{
int len, r;
name[4] = 0;
r = FS_Read(name, 4, f);
if(r != 4)
return -1;
len = FGetLittleLong(f);
if( len < 0 ) {
Com_Printf( S_COLOR_YELLOW "WARNING: Negative chunk length\n" );
return -1;
}
return len;
}
/*
=================
S_FindRIFFChunk
Returns the length of the data in the chunk, or -1 if not found
=================
*/
static int S_FindRIFFChunk( fileHandle_t f, char *chunk ) {
char name[5];
int len;
while( ( len = S_ReadChunkInfo(f, name) ) >= 0 )
{
// If this is the right chunk, return
if( !Q_strncmp( name, chunk, 4 ) )
return len;
len = PAD( len, 2 );
// Not the right chunk - skip it
FS_Seek( f, len, FS_SEEK_CUR );
}
return -1;
}
/*
=================
S_ByteSwapRawSamples
=================
*/
static void S_ByteSwapRawSamples( int samples, int width, int s_channels, const byte *data ) {
int i;
if ( width != 2 ) {
return;
}
if ( LittleShort( 256 ) == 256 ) {
return;
}
if ( s_channels == 2 ) {
samples <<= 1;
}
for ( i = 0 ; i < samples ; i++ ) {
((short *)data)[i] = LittleShort( ((short *)data)[i] );
}
}
/*
=================
S_ReadRIFFHeader
=================
*/
static qboolean S_ReadRIFFHeader(fileHandle_t file, snd_info_t *info)
{
char dump[16];
int bits;
int fmtlen = 0;
// skip the riff wav header
FS_Read(dump, 12, file);
// Scan for the format chunk
if((fmtlen = S_FindRIFFChunk(file, "fmt ")) < 0)
{
Com_Printf( S_COLOR_RED "ERROR: Couldn't find \"fmt\" chunk\n");
return qfalse;
}
// Save the parameters
FGetLittleShort(file); // wav_format
info->channels = FGetLittleShort(file);
info->rate = FGetLittleLong(file);
FGetLittleLong(file);
FGetLittleShort(file);
bits = FGetLittleShort(file);
if( bits < 8 )
{
Com_Printf( S_COLOR_RED "ERROR: Less than 8 bit sound is not supported\n");
return qfalse;
}
info->width = bits / 8;
info->dataofs = 0;
// Skip the rest of the format chunk if required
if(fmtlen > 16)
{
fmtlen -= 16;
FS_Seek( file, fmtlen, FS_SEEK_CUR );
}
// Scan for the data chunk
if( (info->size = S_FindRIFFChunk(file, "data")) < 0)
{
Com_Printf( S_COLOR_RED "ERROR: Couldn't find \"data\" chunk\n");
return qfalse;
}
info->samples = (info->size / info->width) / info->channels;
return qtrue;
}
// WAV codec
snd_codec_t wav_codec =
{
"wav",
S_WAV_CodecLoad,
S_WAV_CodecOpenStream,
S_WAV_CodecReadStream,
S_WAV_CodecCloseStream,
NULL
};
/*
=================
S_WAV_CodecLoad
=================
*/
void *S_WAV_CodecLoad(const char *filename, snd_info_t *info)
{
fileHandle_t file;
void *buffer;
// Try to open the file
FS_FOpenFileRead(filename, &file, qtrue, qtrue);
if(!file)
{
return NULL;
}
// Read the RIFF header
if(!S_ReadRIFFHeader(file, info))
{
FS_FCloseFile(file);
Com_Printf( S_COLOR_RED "ERROR: Incorrect/unsupported format in \"%s\"\n",
filename);
return NULL;
}
// Allocate some memory
buffer = Hunk_AllocateTempMemory(info->size);
if(!buffer)
{
FS_FCloseFile(file);
Com_Printf( S_COLOR_RED "ERROR: Out of memory reading \"%s\"\n",
filename);
return NULL;
}
// Read, byteswap
FS_Read(buffer, info->size, file);
S_ByteSwapRawSamples(info->samples, info->width, info->channels, (byte *)buffer);
// Close and return
FS_FCloseFile(file);
return buffer;
}
/*
=================
S_WAV_CodecOpenStream
=================
*/
snd_stream_t *S_WAV_CodecOpenStream(const char *filename)
{
snd_stream_t *rv;
// Open
rv = S_CodecUtilOpen(filename, &wav_codec);
if(!rv)
return NULL;
// Read the RIFF header
if(!S_ReadRIFFHeader(rv->file, &rv->info))
{
S_CodecUtilClose(&rv);
return NULL;
}
return rv;
}
/*
=================
S_WAV_CodecCloseStream
=================
*/
void S_WAV_CodecCloseStream(snd_stream_t *stream)
{
S_CodecUtilClose(&stream);
}
/*
=================
S_WAV_CodecReadStream
=================
*/
int S_WAV_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer)
{
int remaining = stream->info.size - stream->pos;
int samples;
if(remaining <= 0)
return 0;
if(bytes > remaining)
bytes = remaining;
stream->pos += bytes;
samples = (bytes / stream->info.width) / stream->info.channels;
FS_Read(buffer, bytes, stream->file);
S_ByteSwapRawSamples(samples, stream->info.width, stream->info.channels, buffer);
return bytes;
}
+789 -438
View File
File diff suppressed because it is too large. Load diff
+24 -87
View File
@@ -15,14 +15,14 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// snd_local.h -- private sound definations
#include "../qcommon/q_shared.h"
#include "q_shared.h"
#include "../qcommon/qcommon.h"
#include "snd_public.h"
@@ -30,9 +30,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
extern "C" {
#endif
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define PAINTBUFFER_SIZE 4096 // this is in samples
#define SND_CHUNK_SIZE 1024 // samples
@@ -63,7 +60,6 @@ typedef struct sfx_s {
qboolean soundCompressed; // not in Memory
int soundCompressionMethod;
int soundLength;
int soundChannels;
char soundName[MAX_QPATH];
int lastTimeUsed;
struct sfx_s *next;
@@ -72,20 +68,14 @@ typedef struct sfx_s {
typedef struct {
int channels;
int samples; // mono samples in buffer
int fullsamples; // samples with all channels in buffer (samples divided by channels)
int submission_chunk; // don't mix less than this #
int samplebits;
int isfloat;
int speed;
byte *buffer;
} dma_t;
#define START_SAMPLE_IMMEDIATE 0x7fffffff
#define MAX_DOPPLER_SCALE 50.0f //arbitrary
#define THIRD_PERSON_THRESHOLD_SQ (48.0f*48.0f)
typedef struct loopSound_s {
vec3_t origin;
vec3_t velocity;
@@ -114,7 +104,6 @@ typedef struct
qboolean fixed_origin; // use origin instead of fetching entnum's origin
sfx_t *thesfx; // sfx structure
qboolean doppler;
qboolean fullVolume;
} channel_t;
@@ -130,38 +119,6 @@ typedef struct {
int dataofs; // chunk starts this many bytes from file start
} wavinfo_t;
// Interface between Q3 sound "api" and the sound backend
typedef struct
{
void (*Shutdown)(void);
void (*StartSound)(const vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx );
void (*StartLocalSound)( sfxHandle_t sfx, int channelNum );
void (*StartBackgroundTrack)( const char *intro, const char *loop );
void (*StopBackgroundTrack)( void );
void (*RawSamples)(int stream, int samples, int rate, int width, int channels, const byte *data, float volume, int entityNum);
void (*StopAllSounds)( void );
void (*ClearLoopingSounds)( qboolean killall );
void (*AddLoopingSound)( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx );
void (*AddRealLoopingSound)( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx );
void (*StopLoopingSound)(int entityNum );
void (*Respatialize)( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater );
void (*UpdateEntityPosition)( int entityNum, const vec3_t origin );
void (*Update)( void );
void (*DisableSounds)( void );
void (*BeginRegistration)( void );
sfxHandle_t (*RegisterSound)( const char *sample, qboolean compressed );
void (*ClearSoundBuffer)( void );
void (*SoundInfo)( void );
void (*SoundList)( void );
#ifdef USE_VOIP
void (*StartCapture)( void );
int (*AvailableCaptureSamples)( void );
void (*Capture)( int samples, byte *data );
void (*StopCapture)( void );
void (*MasterGain)( float gain );
#endif
} soundInterface_t;
/*
====================================================================
@@ -184,51 +141,43 @@ void SNDDMA_BeginPainting (void);
void SNDDMA_Submit(void);
#ifdef USE_VOIP
void SNDDMA_StartCapture(void);
int SNDDMA_AvailableCaptureSamples(void);
void SNDDMA_Capture(int samples, byte *data);
void SNDDMA_StopCapture(void);
void SNDDMA_MasterGain(float val);
#endif
//====================================================================
#define MAX_SOUNDCHANNELS 96
#define MAX_CHANNELS 96
extern channel_t s_channels[MAX_SOUNDCHANNELS];
extern channel_t loop_channels[MAX_SOUNDCHANNELS];
extern channel_t s_channels[MAX_CHANNELS];
extern channel_t loop_channels[MAX_CHANNELS];
extern int numLoopChannels;
extern int s_paintedtime;
extern int s_rawend;
extern vec3_t listener_forward;
extern vec3_t listener_right;
extern vec3_t listener_up;
extern dma_t dma;
#define MAX_RAW_SAMPLES 16384
#define MAX_RAW_STREAMS (MAX_CLIENTS * 2 + 1)
extern portable_samplepair_t s_rawsamples[MAX_RAW_STREAMS][MAX_RAW_SAMPLES];
extern int s_rawend[MAX_RAW_STREAMS];
extern portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES];
extern cvar_t *s_volume;
extern cvar_t *s_musicVolume;
extern cvar_t *s_muted;
extern cvar_t *s_doppler;
extern cvar_t *s_volume;
extern cvar_t *s_nosound;
extern cvar_t *s_khz;
extern cvar_t *s_show;
extern cvar_t *s_mixahead;
extern cvar_t *s_testsound;
extern cvar_t *s_testsound;
extern cvar_t *s_separation;
qboolean S_LoadSound( sfx_t *sfx );
void SND_free(sndBuffer *v);
sndBuffer* SND_malloc( void );
void SND_setup( void );
void SND_shutdown(void);
sndBuffer* SND_malloc();
void SND_setup();
void S_PaintChannels(int endtime);
void S_memoryLoad(sfx_t *sfx);
portable_samplepair_t *S_GetRawSamplePointer();
// spatializes a channel
void S_Spatialize(channel_t *ch);
@@ -243,7 +192,7 @@ void S_AdpcmGetSamples(sndBuffer *chunk, short *to);
#define SENTINEL_MULAW_ZERO_RUN 127
#define SENTINEL_MULAW_FOUR_BIT_RUN 126
void S_FreeOldestSound( void );
void S_FreeOldestSound();
#define NXStream byte
@@ -257,24 +206,12 @@ extern short *sfxScratchBuffer;
extern sfx_t *sfxScratchPointer;
extern int sfxScratchIndex;
qboolean S_Base_Init( soundInterface_t *si );
// OpenAL stuff
typedef enum
{
SRCPRI_AMBIENT = 0, // Ambient sound effects
SRCPRI_ENTITY, // Entity sound effects
SRCPRI_ONESHOT, // One-shot sounds
SRCPRI_LOCAL, // Local sounds
SRCPRI_STREAM // Streams (music, cutscenes)
} alSrcPriority_t;
typedef int srcHandle_t;
qboolean S_AL_Init( soundInterface_t *si );
#ifdef idppc_altivec
void S_PaintChannelFrom16_altivec( portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE], int snd_vol, channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset );
#ifdef __linux__
// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371
// custom Snd_Memset implementation for glibc memset bug workaround
void Snd_Memset(void* dest, const int val, const size_t count);
#else
#define Snd_Memset Com_Memset
#endif
#ifdef __cplusplus
+6 -6
View File
@@ -22,18 +22,19 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "client.h"
#include "snd_codec.h"
#include "snd_local.h"
#include "snd_public.h"
cvar_t *s_volume;
cvar_t *s_muted;
cvar_t *s_musicVolume;
cvar_t *s_doppler;
cvar_t *s_backend;
cvar_t *s_muteWhenMinimized;
cvar_t *s_muteWhenUnfocused;
#if 0
cvar_t* s_volume;
cvar_t* s_musicVolume;
cvar_t* s_doppler;
static soundInterface_t si;
/*
@@ -332,13 +333,11 @@ S_RegisterSound
*/
sfxHandle_t S_RegisterSound(const char *sample, qboolean compressed, qboolean streamed)
{
/*
if( si.RegisterSound ) {
return si.RegisterSound( sample, compressed );
} else {
return 0;
}
*/
// FIXME: infinite loop
}
@@ -889,3 +888,4 @@ void S_FadeSound(float fTime)
{
// FIXME: stub
}
#endif
+196 -66
View File
@@ -15,7 +15,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
@@ -30,7 +30,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*****************************************************************************/
#include "snd_local.h"
#include "snd_codec.h"
#define DEF_COMSOUNDMEGS "8"
@@ -57,7 +56,7 @@ void SND_free(sndBuffer *v) {
inUse += sizeof(sndBuffer);
}
sndBuffer* SND_malloc(void) {
sndBuffer* SND_malloc() {
sndBuffer *v;
redo:
if (freelist == NULL) {
@@ -74,7 +73,7 @@ redo:
return v;
}
void SND_setup(void) {
void SND_setup() {
sndBuffer *p, *q;
cvar_t *cv;
int scs;
@@ -100,12 +99,137 @@ void SND_setup(void) {
Com_Printf("Sound memory manager started\n");
}
void SND_shutdown(void)
/*
===============================================================================
WAV loading
===============================================================================
*/
static byte *data_p;
static byte *iff_end;
static byte *last_chunk;
static byte *iff_data;
static int iff_chunk_len;
static short GetLittleShort(void)
{
free(sfxScratchBuffer);
free(buffer);
short val = 0;
val = *data_p;
val = val + (*(data_p+1)<<8);
data_p += 2;
return val;
}
static int GetLittleLong(void)
{
int val = 0;
val = *data_p;
val = val + (*(data_p+1)<<8);
val = val + (*(data_p+2)<<16);
val = val + (*(data_p+3)<<24);
data_p += 4;
return val;
}
static void FindNextChunk(char *name)
{
while (1)
{
data_p=last_chunk;
if (data_p >= iff_end)
{ // didn't find the chunk
data_p = NULL;
return;
}
data_p += 4;
iff_chunk_len = GetLittleLong();
if (iff_chunk_len < 0)
{
data_p = NULL;
return;
}
data_p -= 8;
last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
if (!strncmp((char *)data_p, name, 4))
return;
}
}
static void FindChunk(char *name)
{
last_chunk = iff_data;
FindNextChunk (name);
}
/*
============
GetWavinfo
============
*/
static wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
{
wavinfo_t info;
Com_Memset (&info, 0, sizeof(info));
if (!wav)
return info;
iff_data = wav;
iff_end = wav + wavlength;
// find "RIFF" chunk
FindChunk("RIFF");
if (!(data_p && !strncmp((char *)data_p+8, "WAVE", 4)))
{
Com_Printf("Missing RIFF/WAVE chunks\n");
return info;
}
// get "fmt " chunk
iff_data = data_p + 12;
// DumpChunks ();
FindChunk("fmt ");
if (!data_p)
{
Com_Printf("Missing fmt chunk\n");
return info;
}
data_p += 8;
info.format = GetLittleShort();
info.channels = GetLittleShort();
info.rate = GetLittleLong();
data_p += 4+2;
info.width = GetLittleShort() / 8;
if (info.format != 1)
{
Com_Printf("Microsoft PCM format only\n");
return info;
}
// find data chunk
FindChunk("data");
if (!data_p)
{
Com_Printf("Missing data chunk\n");
return info;
}
data_p += 4;
info.samples = GetLittleLong () / info.width;
info.dataofs = data_p - wav;
return info;
}
/*
================
ResampleSfx
@@ -113,53 +237,47 @@ ResampleSfx
resample / decimate to the current source rate
================
*/
static int ResampleSfx( sfx_t *sfx, int channels, int inrate, int inwidth, int samples, byte *data, qboolean compressed ) {
static void ResampleSfx( sfx_t *sfx, int inrate, int inwidth, byte *data, qboolean compressed ) {
int outcount;
int srcsample;
float stepscale;
int i, j;
int i;
int sample, samplefrac, fracstep;
int part;
sndBuffer *chunk;
stepscale = (float)inrate / dma.speed; // this is usually 0.5, 1, or 2
outcount = samples / stepscale;
outcount = sfx->soundLength / stepscale;
sfx->soundLength = outcount;
srcsample = 0;
samplefrac = 0;
fracstep = stepscale * 256 * channels;
fracstep = stepscale * 256;
chunk = sfx->soundData;
for (i=0 ; i<outcount ; i++)
{
srcsample += samplefrac >> 8;
samplefrac &= 255;
srcsample = samplefrac >> 8;
samplefrac += fracstep;
for (j=0 ; j<channels ; j++)
{
if( inwidth == 2 ) {
sample = ( ((short *)data)[srcsample+j] );
if( inwidth == 2 ) {
sample = LittleShort ( ((short *)data)[srcsample] );
} else {
sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
}
part = (i&(SND_CHUNK_SIZE-1));
if (part == 0) {
sndBuffer *newchunk;
newchunk = SND_malloc();
if (chunk == NULL) {
sfx->soundData = newchunk;
} else {
sample = (unsigned int)( (unsigned char)(data[srcsample+j]) - 128) << 8;
chunk->next = newchunk;
}
part = (i*channels+j)&(SND_CHUNK_SIZE-1);
if (part == 0) {
sndBuffer *newchunk;
newchunk = SND_malloc();
if (chunk == NULL) {
sfx->soundData = newchunk;
} else {
chunk->next = newchunk;
}
chunk = newchunk;
}
chunk->sndChunk[part] = sample;
chunk = newchunk;
}
}
return outcount;
chunk->sndChunk[part] = sample;
}
}
/*
@@ -169,39 +287,35 @@ ResampleSfx
resample / decimate to the current source rate
================
*/
static int ResampleSfxRaw( short *sfx, int channels, int inrate, int inwidth, int samples, byte *data ) {
static int ResampleSfxRaw( short *sfx, int inrate, int inwidth, int samples, byte *data ) {
int outcount;
int srcsample;
float stepscale;
int i, j;
int i;
int sample, samplefrac, fracstep;
stepscale = (float)inrate / dma.speed; // this is usually 0.5, 1, or 2
outcount = samples / stepscale;
srcsample = 0;
samplefrac = 0;
fracstep = stepscale * 256 * channels;
fracstep = stepscale * 256;
for (i=0 ; i<outcount ; i++)
{
srcsample += samplefrac >> 8;
samplefrac &= 255;
srcsample = samplefrac >> 8;
samplefrac += fracstep;
for (j=0 ; j<channels ; j++)
{
if( inwidth == 2 ) {
sample = LittleShort ( ((short *)data)[srcsample+j] );
} else {
sample = (int)( (unsigned char)(data[srcsample+j]) - 128) << 8;
}
sfx[i*channels+j] = sample;
if( inwidth == 2 ) {
sample = LittleShort ( ((short *)data)[srcsample] );
} else {
sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
}
sfx[i] = sample;
}
return outcount;
}
//=============================================================================
/*
@@ -216,23 +330,40 @@ qboolean S_LoadSound( sfx_t *sfx )
{
byte *data;
short *samples;
snd_info_t info;
// int size;
wavinfo_t info;
int size;
// player specific sounds are never directly loaded
if ( sfx->soundName[0] == '*') {
return qfalse;
}
// load it in
data = S_CodecLoad(sfx->soundName, &info);
if(!data)
size = FS_ReadFile( sfx->soundName, (void **)&data );
if ( !data ) {
return qfalse;
}
info = GetWavinfo( sfx->soundName, data, size );
if ( info.channels != 1 ) {
Com_Printf ("%s is a stereo wav file\n", sfx->soundName);
FS_FreeFile (data);
return qfalse;
}
if ( info.width == 1 ) {
Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is a 8 bit audio file\n", sfx->soundName);
Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is a 8 bit wav file\n", sfx->soundName);
}
if ( info.rate != 22050 ) {
Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is not a 22kHz audio file\n", sfx->soundName);
Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is not a 22kHz wav file\n", sfx->soundName);
}
samples = Hunk_AllocateTempMemory(info.channels * info.samples * sizeof(short) * 2);
if (!info.samples) {
return qfalse;
}
samples = Hunk_AllocateTempMemory(info.samples * sizeof(short) * 2);
sfx->lastTimeUsed = Com_Milliseconds()+1;
@@ -242,37 +373,36 @@ qboolean S_LoadSound( sfx_t *sfx )
// manager to do the right thing for us and page
// sound in as needed
if( info.channels == 1 && sfx->soundCompressed == qtrue) {
if( sfx->soundCompressed == qtrue) {
sfx->soundCompressionMethod = 1;
sfx->soundData = NULL;
sfx->soundLength = ResampleSfxRaw( samples, info.channels, info.rate, info.width, info.samples, data + info.dataofs );
sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) );
S_AdpcmEncodeSound(sfx, samples);
#if 0
} else if (info.channels == 1 && info.samples>(SND_CHUNK_SIZE*16) && info.width >1) {
} else if (info.samples>(SND_CHUNK_SIZE*16) && info.width >1) {
sfx->soundCompressionMethod = 3;
sfx->soundData = NULL;
sfx->soundLength = ResampleSfxRaw( samples, info.channels, info.rate, info.width, info.samples, (data + info.dataofs) );
sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) );
encodeMuLaw( sfx, samples);
} else if (info.channels == 1 && info.samples>(SND_CHUNK_SIZE*6400) && info.width >1) {
} else if (info.samples>(SND_CHUNK_SIZE*6400) && info.width >1) {
sfx->soundCompressionMethod = 2;
sfx->soundData = NULL;
sfx->soundLength = ResampleSfxRaw( samples, info.channels, info.rate, info.width, info.samples, (data + info.dataofs) );
sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) );
encodeWavelet( sfx, samples);
#endif
} else {
sfx->soundCompressionMethod = 0;
sfx->soundLength = info.samples;
sfx->soundData = NULL;
sfx->soundLength = ResampleSfx( sfx, info.channels, info.rate, info.width, info.samples, data + info.dataofs, qfalse );
ResampleSfx( sfx, info.rate, info.width, data + info.dataofs, qfalse );
}
sfx->soundChannels = info.channels;
Hunk_FreeTempMemory(samples);
Hunk_FreeTempMemory(data);
FS_FreeFile( data );
return qtrue;
}
void S_DisplayFreeMemory(void) {
void S_DisplayFreeMemory() {
Com_Printf("%d bytes free sound buffer memory, %d total used\n", inUse, totalInUse);
}
+182 -128
View File
@@ -15,23 +15,24 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// snd_mix.c -- portable code to mix sounds for snd_dma.c
#include "client.h"
#include "snd_local.h"
static portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE];
static int snd_vol;
// bk001119 - these not static, required by unix/snd_mixa.s
int* snd_p;
int snd_linear_count;
short* snd_out;
#if !id386 // if configured not to use asm
#if !( (defined __linux__ || defined __FreeBSD__ ) && (defined __i386__) ) // rb010123
#if !id386
void S_WriteLinearBlastStereo16 (void)
{
@@ -57,9 +58,6 @@ void S_WriteLinearBlastStereo16 (void)
snd_out[i+1] = val;
}
}
#elif defined(__GNUC__)
// uses snd_mixa.s
void S_WriteLinearBlastStereo16 (void);
#else
__declspec( naked ) void S_WriteLinearBlastStereo16 (void)
@@ -106,6 +104,10 @@ LClampDone2:
}
}
#endif
#else
// forward declare, implementation somewhere else
void S_WriteLinearBlastStereo16 (void);
#endif
void S_TransferStereo16 (unsigned long *pbuf, int endtime)
@@ -119,24 +121,21 @@ void S_TransferStereo16 (unsigned long *pbuf, int endtime)
while (ls_paintedtime < endtime)
{
// handle recirculating buffer issues
lpos = ls_paintedtime % dma.fullsamples;
lpos = ls_paintedtime & ((dma.samples>>1)-1);
snd_out = (short *) pbuf + (lpos<<1); // lpos * dma.channels
snd_out = (short *) pbuf + (lpos<<1);
snd_linear_count = dma.fullsamples - lpos;
snd_linear_count = (dma.samples>>1) - lpos;
if (ls_paintedtime + snd_linear_count > endtime)
snd_linear_count = endtime - ls_paintedtime;
snd_linear_count <<= 1; // snd_linear_count *= dma.channels
snd_linear_count <<= 1;
// write a linear blast of samples
S_WriteLinearBlastStereo16 ();
snd_p += snd_linear_count;
ls_paintedtime += (snd_linear_count>>1); // snd_linear_count / dma.channels
if( CL_VideoRecording( ) )
CL_WriteAVIAudioFrame( (byte *)snd_out, snd_linear_count << 1 ); // snd_linear_count * (dma.samplebits/8)
ls_paintedtime += (snd_linear_count>>1);
}
}
@@ -150,16 +149,19 @@ void S_TransferPaintBuffer(int endtime)
{
int out_idx;
int count;
int out_mask;
int *p;
int step;
int val;
int i;
unsigned long *pbuf;
pbuf = (unsigned long *)dma.buffer;
if ( s_testsound->integer ) {
int i;
int count;
// write a fixed sine wave
count = (endtime - s_paintedtime);
for (i=0 ; i<count ; i++)
@@ -175,73 +177,38 @@ void S_TransferPaintBuffer(int endtime)
{ // general case
p = (int *) paintbuffer;
count = (endtime - s_paintedtime) * dma.channels;
out_idx = ((unsigned int)s_paintedtime * dma.channels) % dma.samples;
step = 3 - MIN(dma.channels, 2);
out_mask = dma.samples - 1;
out_idx = s_paintedtime * dma.channels & out_mask;
step = 3 - dma.channels;
if ((dma.isfloat) && (dma.samplebits == 32))
{
float *out = (float *) pbuf;
for (i=0 ; i<count ; i++)
{
if ((i % dma.channels) >= 2)
{
val = 0;
}
else
{
val = *p >> 8;
p+= step;
}
if (val > 0x7fff)
val = 0x7fff;
else if (val < -32767) /* clamp to one less than max to make division max out at -1.0f. */
val = -32767;
out[out_idx] = ((float) val) / 32767.0f;
out_idx = (out_idx + 1) % dma.samples;
}
}
else if (dma.samplebits == 16)
if (dma.samplebits == 16)
{
short *out = (short *) pbuf;
for (i=0 ; i<count ; i++)
while (count--)
{
if ((i % dma.channels) >= 2)
{
val = 0;
}
else
{
val = *p >> 8;
p+= step;
}
val = *p >> 8;
p+= step;
if (val > 0x7fff)
val = 0x7fff;
else if (val < -32768)
val = -32768;
out[out_idx] = val;
out_idx = (out_idx + 1) % dma.samples;
out_idx = (out_idx + 1) & out_mask;
}
}
else if (dma.samplebits == 8)
{
unsigned char *out = (unsigned char *) pbuf;
for (i=0 ; i<count ; i++)
while (count--)
{
if ((i % dma.channels) >= 2)
{
val = 0;
}
else
{
val = *p >> 8;
p+= step;
}
val = *p >> 8;
p+= step;
if (val > 0x7fff)
val = 0x7fff;
else if (val < -32768)
val = -32768;
out[out_idx] = (val>>8) + 128;
out_idx = (out_idx + 1) % dma.samples;
out_idx = (out_idx + 1) & out_mask;
}
}
}
@@ -256,18 +223,14 @@ CHANNEL MIXING
===============================================================================
*/
static void S_PaintChannelFrom16_scalar( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
static void S_PaintChannelFrom16( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
int data, aoff, boff;
int leftvol, rightvol;
int i, j;
portable_samplepair_t *samp;
sndBuffer *chunk;
short *samples;
float ooff, fdata[2], fdiv, fleftvol, frightvol;
if (sc->soundChannels <= 0) {
return;
}
float ooff, fdata, fdiv, fleftvol, frightvol;
samp = &paintbuffer[ bufferOffset ];
@@ -275,14 +238,6 @@ static void S_PaintChannelFrom16_scalar( channel_t *ch, const sfx_t *sc, int cou
sampleOffset = sampleOffset*ch->oldDopplerScale;
}
if ( sc->soundChannels == 2 ) {
sampleOffset *= sc->soundChannels;
if ( sampleOffset & 1 ) {
sampleOffset &= ~1;
}
}
chunk = sc->soundData;
while (sampleOffset>=SND_CHUNK_SIZE) {
chunk = chunk->next;
@@ -293,16 +248,126 @@ static void S_PaintChannelFrom16_scalar( channel_t *ch, const sfx_t *sc, int cou
}
if (!ch->doppler || ch->dopplerScale==1.0f) {
#if idppc_altivec
vector signed short volume_vec;
vector unsigned int volume_shift;
int vectorCount, samplesLeft, chunkSamplesLeft;
#endif
leftvol = ch->leftvol*snd_vol;
rightvol = ch->rightvol*snd_vol;
samples = chunk->sndChunk;
#if idppc_altivec
((short *)&volume_vec)[0] = leftvol;
((short *)&volume_vec)[1] = leftvol;
((short *)&volume_vec)[4] = leftvol;
((short *)&volume_vec)[5] = leftvol;
((short *)&volume_vec)[2] = rightvol;
((short *)&volume_vec)[3] = rightvol;
((short *)&volume_vec)[6] = rightvol;
((short *)&volume_vec)[7] = rightvol;
volume_shift = vec_splat_u32(8);
i = 0;
while(i < count) {
/* Try to align destination to 16-byte boundary */
while(i < count && (((unsigned long)&samp[i] & 0x1f) || ((count-i) < 8) || ((SND_CHUNK_SIZE - sampleOffset) < 8))) {
data = samples[sampleOffset++];
samp[i].left += (data * leftvol)>>8;
samp[i].right += (data * rightvol)>>8;
if (sampleOffset == SND_CHUNK_SIZE) {
chunk = chunk->next;
samples = chunk->sndChunk;
sampleOffset = 0;
}
i++;
}
/* Destination is now aligned. Process as many 8-sample
chunks as we can before we run out of room from the current
sound chunk. We do 8 per loop to avoid extra source data reads. */
samplesLeft = count - i;
chunkSamplesLeft = SND_CHUNK_SIZE - sampleOffset;
if(samplesLeft > chunkSamplesLeft)
samplesLeft = chunkSamplesLeft;
vectorCount = samplesLeft / 8;
if(vectorCount)
{
vector unsigned char tmp;
vector short s0, s1, sampleData0, sampleData1;
vector short samples0, samples1;
vector signed int left0, right0;
vector signed int merge0, merge1;
vector signed int d0, d1, d2, d3;
vector unsigned char samplePermute0 =
(vector unsigned char)(0, 1, 4, 5, 0, 1, 4, 5, 2, 3, 6, 7, 2, 3, 6, 7);
vector unsigned char samplePermute1 =
(vector unsigned char)(8, 9, 12, 13, 8, 9, 12, 13, 10, 11, 14, 15, 10, 11, 14, 15);
vector unsigned char loadPermute0, loadPermute1;
// Rather than permute the vectors after we load them to do the sample
// replication and rearrangement, we permute the alignment vector so
// we do everything in one step below and avoid data shuffling.
tmp = vec_lvsl(0,&samples[sampleOffset]);
loadPermute0 = vec_perm(tmp,tmp,samplePermute0);
loadPermute1 = vec_perm(tmp,tmp,samplePermute1);
s0 = *(vector short *)&samples[sampleOffset];
while(vectorCount)
{
/* Load up source (16-bit) sample data */
s1 = *(vector short *)&samples[sampleOffset+7];
/* Load up destination sample data */
d0 = *(vector signed int *)&samp[i];
d1 = *(vector signed int *)&samp[i+2];
d2 = *(vector signed int *)&samp[i+4];
d3 = *(vector signed int *)&samp[i+6];
sampleData0 = vec_perm(s0,s1,loadPermute0);
sampleData1 = vec_perm(s0,s1,loadPermute1);
merge0 = vec_mule(sampleData0,volume_vec);
merge0 = vec_sra(merge0,volume_shift); /* Shift down to proper range */
merge1 = vec_mulo(sampleData0,volume_vec);
merge1 = vec_sra(merge1,volume_shift);
d0 = vec_add(merge0,d0);
d1 = vec_add(merge1,d1);
merge0 = vec_mule(sampleData1,volume_vec);
merge0 = vec_sra(merge0,volume_shift); /* Shift down to proper range */
merge1 = vec_mulo(sampleData1,volume_vec);
merge1 = vec_sra(merge1,volume_shift);
d2 = vec_add(merge0,d2);
d3 = vec_add(merge1,d3);
/* Store destination sample data */
*(vector signed int *)&samp[i] = d0;
*(vector signed int *)&samp[i+2] = d1;
*(vector signed int *)&samp[i+4] = d2;
*(vector signed int *)&samp[i+6] = d3;
i += 8;
vectorCount--;
s0 = s1;
sampleOffset += 8;
}
if (sampleOffset == SND_CHUNK_SIZE) {
chunk = chunk->next;
samples = chunk->sndChunk;
sampleOffset = 0;
}
}
}
#else
for ( i=0 ; i<count ; i++ ) {
data = samples[sampleOffset++];
samp[i].left += (data * leftvol)>>8;
if ( sc->soundChannels == 2 ) {
data = samples[sampleOffset++];
}
samp[i].right += (data * rightvol)>>8;
if (sampleOffset == SND_CHUNK_SIZE) {
@@ -311,6 +376,7 @@ static void S_PaintChannelFrom16_scalar( channel_t *ch, const sfx_t *sc, int cou
sampleOffset = 0;
}
}
#endif
} else {
fleftvol = ch->leftvol*snd_vol;
frightvol = ch->rightvol*snd_vol;
@@ -324,10 +390,10 @@ static void S_PaintChannelFrom16_scalar( channel_t *ch, const sfx_t *sc, int cou
for ( i=0 ; i<count ; i++ ) {
aoff = ooff;
ooff = ooff + ch->dopplerScale * sc->soundChannels;
ooff = ooff + ch->dopplerScale;
boff = ooff;
fdata[0] = fdata[1] = 0;
for (j=aoff; j<boff; j += sc->soundChannels) {
fdata = 0;
for (j=aoff; j<boff; j++) {
if (j == SND_CHUNK_SIZE) {
chunk = chunk->next;
if (!chunk) {
@@ -336,32 +402,15 @@ static void S_PaintChannelFrom16_scalar( channel_t *ch, const sfx_t *sc, int cou
samples = chunk->sndChunk;
ooff -= SND_CHUNK_SIZE;
}
if ( sc->soundChannels == 2 ) {
fdata[0] += samples[j&(SND_CHUNK_SIZE-1)];
fdata[1] += samples[(j+1)&(SND_CHUNK_SIZE-1)];
} else {
fdata[0] += samples[j&(SND_CHUNK_SIZE-1)];
fdata[1] += samples[j&(SND_CHUNK_SIZE-1)];
}
fdata += samples[j&(SND_CHUNK_SIZE-1)];
}
fdiv = 256 * (boff-aoff) / sc->soundChannels;
samp[i].left += (fdata[0] * fleftvol)/fdiv;
samp[i].right += (fdata[1] * frightvol)/fdiv;
fdiv = 256 * (boff-aoff);
samp[i].left += (fdata * fleftvol)/fdiv;
samp[i].right += (fdata * frightvol)/fdiv;
}
}
}
static void S_PaintChannelFrom16( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
#if idppc_altivec
if (com_altivec->integer) {
// must be in a separate translation unit or G3 systems will crash.
S_PaintChannelFrom16_altivec( paintbuffer, snd_vol, ch, sc, count, sampleOffset, bufferOffset );
return;
}
#endif
S_PaintChannelFrom16_scalar( ch, sc, count, sampleOffset, bufferOffset );
}
void S_PaintChannelFromWavelet( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
int data;
int leftvol, rightvol;
@@ -383,7 +432,7 @@ void S_PaintChannelFromWavelet( channel_t *ch, sfx_t *sc, int count, int sampleO
}
if (i!=sfxScratchIndex || sfxScratchPointer != sc) {
decodeWavelet(chunk, sfxScratchBuffer);
S_AdpcmGetSamples( chunk, sfxScratchBuffer );
sfxScratchIndex = i;
sfxScratchPointer = sc;
}
@@ -480,7 +529,7 @@ void S_PaintChannelFromMuLaw( channel_t *ch, sfx_t *sc, int count, int sampleOff
samp[i].left += (data * leftvol)>>8;
samp[i].right += (data * rightvol)>>8;
samples++;
if (chunk != NULL && samples == (byte *)chunk->sndChunk+(SND_CHUNK_SIZE*2)) {
if (samples == (byte *)chunk->sndChunk+(SND_CHUNK_SIZE*2)) {
chunk = chunk->next;
samples = (byte *)chunk->sndChunk;
}
@@ -513,16 +562,13 @@ S_PaintChannels
void S_PaintChannels( int endtime ) {
int i;
int end;
int stream;
channel_t *ch;
sfx_t *sc;
int ltime, count;
int sampleOffset;
if(s_muted->integer)
snd_vol = 0;
else
snd_vol = s_volume->value*255;
snd_vol = s_volume->value*255;
//Com_Printf ("%i to %i\n", s_paintedtime, endtime);
while ( s_paintedtime < endtime ) {
@@ -533,24 +579,36 @@ void S_PaintChannels( int endtime ) {
end = s_paintedtime + PAINTBUFFER_SIZE;
}
// clear the paint buffer and mix any raw samples...
Com_Memset(paintbuffer, 0, sizeof (paintbuffer));
for (stream = 0; stream < MAX_RAW_STREAMS; stream++) {
if ( s_rawend[stream] >= s_paintedtime ) {
// copy from the streaming sound source
const portable_samplepair_t *rawsamples = s_rawsamples[stream];
const int stop = (end < s_rawend[stream]) ? end : s_rawend[stream];
for ( i = s_paintedtime ; i < stop ; i++ ) {
const int s = i&(MAX_RAW_SAMPLES-1);
paintbuffer[i-s_paintedtime].left += rawsamples[s].left;
paintbuffer[i-s_paintedtime].right += rawsamples[s].right;
}
// clear the paint buffer to either music or zeros
if ( s_rawend < s_paintedtime ) {
if ( s_rawend ) {
//Com_DPrintf ("background sound underrun\n");
}
Com_Memset(paintbuffer, 0, (end - s_paintedtime) * sizeof(portable_samplepair_t));
} else {
// copy from the streaming sound source
int s;
int stop;
stop = (end < s_rawend) ? end : s_rawend;
for ( i = s_paintedtime ; i < stop ; i++ ) {
s = i&(MAX_RAW_SAMPLES-1);
paintbuffer[i-s_paintedtime] = s_rawsamples[s];
}
// if (i != end)
// Com_Printf ("partial stream\n");
// else
// Com_Printf ("full stream\n");
for ( ; i < end ; i++ ) {
paintbuffer[i-s_paintedtime].left =
paintbuffer[i-s_paintedtime].right = 0;
}
}
// paint in the channels.
ch = s_channels;
for ( i = 0; i < MAX_SOUNDCHANNELS ; i++, ch++ ) {
for ( i = 0; i < MAX_CHANNELS ; i++, ch++ ) {
if ( !ch->thesfx || (ch->leftvol<0.25 && ch->rightvol<0.25 )) {
continue;
}
@@ -558,10 +616,6 @@ void S_PaintChannels( int endtime ) {
ltime = s_paintedtime;
sc = ch->thesfx;
if (sc->soundData==NULL || sc->soundLength==0) {
continue;
}
sampleOffset = ltime - ch->startSample;
count = end - ltime;
if ( sampleOffset + count > sc->soundLength ) {
File diff suppressed because it is too large. Load diff
+8 -18
View File
@@ -15,7 +15,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
@@ -38,8 +38,8 @@ void S_StopBackgroundTrack( void );
// cinematics and voice-over-network will send raw samples
// 1.0 volume will be direct output of source samples
void S_RawSamples(int stream, int samples, int rate, int width, int channels,
const byte *data, float volume, int entityNum);
void S_RawSamples (int samples, int rate, int width, int channels,
const byte *data, float volume);
// stop all sounds and the background track
void S_StopAllSounds(qboolean stop_music);
@@ -51,21 +51,20 @@ void S_AddLoopingSound(const vec3_t origin, const vec3_t velocity, sfxHandle_t s
void S_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx );
void S_StopLoopingSound(int entityNum );
// recompute the relative volumes for all running sounds
// relative to the given entityNum / orientation
void S_Respatialize(int entityNum, const vec3_t origin, vec3_t axis[3], int inwater);
// recompute the reletive volumes for all running sounds
// reletive to the given entityNum / orientation
void S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater );
// let the sound system know where an entity currently is
void S_UpdateEntityPosition(int entityNum, const vec3_t origin);
void S_UpdateEntityPosition( int entityNum, const vec3_t origin );
void S_Update( void );
void S_DisableSounds( void );
void S_BeginRegistration(void);
void S_BeginRegistration( void );
void S_EndRegistration(void);
qboolean S_IsSoundRegistered(const char* name);
// RegisterSound will allways return a valid sample, even if it
// has to create a placeholder. This prevents continuous filesystem
// checks for missing files
@@ -110,15 +109,6 @@ void S_RespatializeOld(int entityNum, const vec3_t origin, vec3_t axis[3]);
void S_UpdateEntity(int entityNum, const vec3_t origin, const vec3_t velocity, qboolean use_listener);
void S_FadeSound(float fTime);
#ifdef USE_VOIP
void S_StartCapture( void );
int S_AvailableCaptureSamples( void );
void S_Capture( int samples, byte *data );
void S_StopCapture( void );
void S_MasterGain( float gain );
#endif
#ifdef __cplusplus
}
#endif
+18 -17
View File
@@ -15,13 +15,15 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "snd_local.h"
long myftol( float f );
#define C0 0.4829629131445341
#define C1 0.8365163037378079
#define C2 0.2241438680420134
@@ -29,8 +31,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
void daub4(float b[], unsigned long n, int isign)
{
float wksp[4097] = { 0.0f };
#define a(x) b[(x)-1] // numerical recipies so a[1] = b[0]
float wksp[4097];
float *a=b-1; // numerical recipies so a[1] = b[0]
unsigned long nh,nh1,i,j;
@@ -39,23 +41,22 @@ void daub4(float b[], unsigned long n, int isign)
nh1=(nh=n >> 1)+1;
if (isign >= 0) {
for (i=1,j=1;j<=n-3;j+=2,i++) {
wksp[i] = C0*a(j)+C1*a(j+1)+C2*a(j+2)+C3*a(j+3);
wksp[i+nh] = C3*a(j)-C2*a(j+1)+C1*a(j+2)-C0*a(j+3);
wksp[i] = C0*a[j]+C1*a[j+1]+C2*a[j+2]+C3*a[j+3];
wksp[i+nh] = C3*a[j]-C2*a[j+1]+C1*a[j+2]-C0*a[j+3];
}
wksp[i ] = C0*a(n-1)+C1*a(n)+C2*a(1)+C3*a(2);
wksp[i+nh] = C3*a(n-1)-C2*a(n)+C1*a(1)-C0*a(2);
wksp[i ] = C0*a[n-1]+C1*a[n]+C2*a[1]+C3*a[2];
wksp[i+nh] = C3*a[n-1]-C2*a[n]+C1*a[1]-C0*a[2];
} else {
wksp[1] = C2*a(nh)+C1*a(n)+C0*a(1)+C3*a(nh1);
wksp[2] = C3*a(nh)-C0*a(n)+C1*a(1)-C2*a(nh1);
wksp[1] = C2*a[nh]+C1*a[n]+C0*a[1]+C3*a[nh1];
wksp[2] = C3*a[nh]-C0*a[n]+C1*a[1]-C2*a[nh1];
for (i=1,j=3;i<nh;i++) {
wksp[j++] = C2*a(i)+C1*a(i+nh)+C0*a(i+1)+C3*a(i+nh1);
wksp[j++] = C3*a(i)-C0*a(i+nh)+C1*a(i+1)-C2*a(i+nh1);
wksp[j++] = C2*a[i]+C1*a[i+nh]+C0*a[i+1]+C3*a[i+nh1];
wksp[j++] = C3*a[i]-C0*a[i+nh]+C1*a[i+1]-C2*a[i+nh1];
}
}
for (i=1;i<=n;i++) {
a(i)=wksp[i];
a[i]=wksp[i];
}
#undef a
}
void wt1(float a[], unsigned long n, int isign)
@@ -120,7 +121,7 @@ void NXPutc(NXStream *stream, char out) {
void encodeWavelet( sfx_t *sfx, short *packets) {
float wksp[4097] = {0}, temp;
float wksp[4097], temp;
int i, samples, size;
sndBuffer *newchunk, *chunk;
byte *out;
@@ -147,7 +148,7 @@ void encodeWavelet( sfx_t *sfx, short *packets) {
newchunk = SND_malloc();
if (sfx->soundData == NULL) {
sfx->soundData = newchunk;
} else if (chunk != NULL) {
} else {
chunk->next = newchunk;
}
chunk = newchunk;
@@ -170,7 +171,7 @@ void encodeWavelet( sfx_t *sfx, short *packets) {
}
void decodeWavelet(sndBuffer *chunk, short *to) {
float wksp[4097] = {0};
float wksp[4097];
int i;
byte *out;
@@ -216,7 +217,7 @@ void encodeMuLaw( sfx_t *sfx, short *packets) {
newchunk = SND_malloc();
if (sfx->soundData == NULL) {
sfx->soundData = newchunk;
} else if (chunk != NULL) {
} else {
chunk->next = newchunk;
}
chunk = newchunk;
-2
View File
@@ -1,7 +1,5 @@
cmake_minimum_required(VERSION 3.5)
project(gcd)
set(CURRENT_DIR "code/gamespy")
include_directories("${CURRENT_DIR}/common")
+3 -3
View File
@@ -728,7 +728,7 @@ void Alias_ListFindRandomRange(AliasList_t* list, const char* alias, int* min_in
}
} while (r >= l);
if (l >= r)
if (r >= l)
{
numfound = 0;
*min_index = index;
@@ -736,7 +736,7 @@ void Alias_ListFindRandomRange(AliasList_t* list, const char* alias, int* min_in
totalfoundweight = 0.f;
ptr = &list->sorted_list[index];
for (i = 0; i < index; i++, ptr--)
for (i = index + 1; i > 0; i--, ptr--)
{
if (strncmp(convalias, (*ptr)->alias_name, length)) {
break;
@@ -746,7 +746,7 @@ void Alias_ListFindRandomRange(AliasList_t* list, const char* alias, int* min_in
{
foundlist[numfound++] = *ptr;
totalfoundweight += (*ptr)->weight;
*min_index = i;
*min_index = i - 1;
}
}
+1 -1
View File
@@ -30,7 +30,7 @@ extern "C" {
#endif
#define PRODUCT_NAME "OpenMoHAA"
#define PRODUCT_VERSION "0.49-alpha"
#define PRODUCT_VERSION "0.50-alpha"
#define PRODUCT_DATE __DATE__
#if TARGET_GAME_TYPE == 1
+7
View File
@@ -1237,6 +1237,13 @@ char **Sys_ListFiles( const char *directory, const char *extension, const char *
void Sys_FreeFileList( char **list );
void Sys_Sleep(int msec);
void Sys_InitStreamThread(void);
void Sys_ShutdownStreamThread(void);
void Sys_BeginStreamedFile(fileHandle_t f, int readAhead);
void Sys_EndStreamedFile(fileHandle_t f);
int Sys_StreamedRead(void* buffer, int size, int count, fileHandle_t f);
void Sys_StreamSeek(fileHandle_t f, int offset, int origin);
qboolean Sys_LowPhysicalMemory( void );
void SetNormalThreadPriority( void );
+1 -1
View File
@@ -535,7 +535,7 @@ typedef struct cTerraPatch_s {
byte s;
byte t;
float texCoord[ 2 ][ 2 ][ 2 ];
vec2_t texCoord[ 2 ][ 2 ];
char x;
char y;
+3
View File
@@ -90,6 +90,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define GL_TEXTURE2_ARB 0x84C2
#define GL_TEXTURE3_ARB 0x84C3
#define GL_COMBINE 0x8570
#define GL_COMBINE4_NV 0x8503
// NOTE: some Linux platforms would need those prototypes
#if defined(MACOS_X)
typedef void (APIENTRY * PFNGLMULTITEXCOORD1DARBPROC) (GLenum target, GLdouble s);
+4
View File
@@ -188,6 +188,10 @@ void GL_TexEnv( int env )
case GL_ADD:
qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD );
break;
case GL_COMBINE4_NV:
case GL_COMBINE:
qglTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, env);
break;
default:
ri.Error( ERR_DROP, "GL_TexEnv: invalid env '%d' passed\n", env );
break;
+45 -28
View File
@@ -457,7 +457,11 @@ R_UnpackTerraPatch
================
*/
void R_UnpackTerraPatch(cTerraPatch_t* pPacked, cTerraPatchUnpacked_t* pUnpacked) {
int i;
int i, j;
union {
int16_t v;
uint8_t b[2];
} flags;
pUnpacked->byDirty = qfalse;
pUnpacked->visCountCheck = 0;
@@ -468,41 +472,54 @@ void R_UnpackTerraPatch(cTerraPatch_t* pPacked, cTerraPatchUnpacked_t* pUnpacked
Com_Error(ERR_DROP, "invalid map: terrain has lmapScale <= 0");
}
pUnpacked->drawinfo.lmapStep = (pPacked->lmapScale / 64);
pUnpacked->drawinfo.lmapSize = (pPacked->lmapScale * 8) | 1;
pUnpacked->drawinfo.lmapStep = (float)(pPacked->lmapScale / 64);
pUnpacked->drawinfo.lmapSize = (float)((pPacked->lmapScale * 8) + 1);
pUnpacked->s = ((float)pPacked->s + 0.5) * 0.0078125;
pUnpacked->t = ((float)pPacked->t + 0.5) * 0.0078125;
//if( s_worldData.lighting ) {
// pUnpacked->drawinfo.lmData = &s_worldData.lighting[ 3 * ( ( pPacked->t << 7 ) + pPacked->s + ( pPacked->iLightMap << 14 ) ) ];
//} else {
// pUnpacked->drawinfo.lmData = NULL;
//}
// fixme: is it important to have a lightmap data?
pUnpacked->drawinfo.lmData = NULL;
memcpy(pUnpacked->texCoord, pPacked->texCoord, sizeof(pUnpacked->texCoord));
pUnpacked->x0 = ((int)pPacked->x << 6);
pUnpacked->y0 = ((int)pPacked->y << 6);
pUnpacked->z0 = pPacked->iBaseHeight;
pUnpacked->shader = ShaderForShaderNum(pPacked->iShader, pPacked->iLightMap);
pUnpacked->iNorth = pPacked->iNorth;
pUnpacked->iEast = pPacked->iEast;
pUnpacked->iSouth = pPacked->iSouth;
pUnpacked->iWest = pPacked->iWest;
for (i = 0; i < 63; i++)
{
pUnpacked->varTree[0][i / 4].fVariance = pPacked->varTree[0][i].flags >> 12;
pUnpacked->varTree[1][i / 4].fVariance = pPacked->varTree[1][i].flags >> 12;
if( s_worldData.lighting ) {
pUnpacked->drawinfo.lmData = &s_worldData.lighting[ 3 * ( ( pPacked->t << 7 ) + pPacked->s + ( pPacked->iLightMap << 14 ) ) ];
} else {
pUnpacked->drawinfo.lmData = NULL;
}
memcpy(pUnpacked->heightmap, pPacked->heightmap, sizeof(pUnpacked->heightmap));
for (i = 0; i < 2; i++) {
for (j = 0; j < 2; j++) {
pUnpacked->texCoord[i][j][0] = LittleFloat(pPacked->texCoord[i][j][0]);
pUnpacked->texCoord[i][j][1] = LittleFloat(pPacked->texCoord[i][j][1]);
}
}
pUnpacked->x0 = ((int)pPacked->x << 6);
pUnpacked->y0 = ((int)pPacked->y << 6);
pUnpacked->z0 = LittleShort(pPacked->iBaseHeight);
pUnpacked->shader = ShaderForShaderNum(LittleShort(pPacked->iShader), LittleShort(pPacked->iLightMap));
pUnpacked->iNorth = LittleShort(pPacked->iNorth);
pUnpacked->iEast = LittleShort(pPacked->iEast);
pUnpacked->iSouth = LittleShort(pPacked->iSouth);
pUnpacked->iWest = LittleShort(pPacked->iWest);
for (i = 0; i < 63; i++)
{
flags.v = pPacked->varTree[0][i].flags;
flags.b[1] &= 7;
pUnpacked->varTree[0][i].fVariance = LittleShort(flags.v);
pUnpacked->varTree[0][i].s.flags = LittleShort(pPacked->varTree[0][i].flags) >> 12;
flags.v = pPacked->varTree[1][i].flags;
flags.b[1] &= 7;
pUnpacked->varTree[1][i].fVariance = LittleShort(flags.v);
pUnpacked->varTree[1][i].s.flags = LittleShort(pPacked->varTree[1][i].flags) >> 12;
}
for (i = 0; i < sizeof(pUnpacked->heightmap) / sizeof(pUnpacked->heightmap[0]); i++) {
pUnpacked->heightmap[i] = pPacked->heightmap[i];
}
pUnpacked->zmax = 0;
pUnpacked->flags = pPacked->flags;
for (i = 0; i < sizeof(pUnpacked->heightmap); i++)
for (i = 0; i < sizeof(pUnpacked->heightmap) / sizeof(pUnpacked->heightmap[0]); i++)
{
if (pUnpacked->zmax < pUnpacked->heightmap[i]) {
pUnpacked->zmax = pUnpacked->heightmap[i];
+6
View File
@@ -1200,6 +1200,10 @@ void R_Register( void )
ri.Cmd_AddCommand( "gfxinfo", GfxInfo_f );
}
void R_InitExtensions() {
// FIXME: unimplemented
}
/*
===============
R_Init
@@ -1292,6 +1296,8 @@ void R_Init( void ) {
InitOpenGL();
R_InitExtensions();
R_InitImages();
R_StartupShaders();
+19 -9
View File
@@ -441,7 +441,7 @@ typedef struct {
qboolean active;
textureBundle_t bundle[NUM_TEXTURE_BUNDLES];
int multitextureEnv;
int multitextureEnv; // 0, GL_MODULATE, GL_ADD (FIXME: put in stage)
waveForm_t rgbWave;
colorGen_t rgbGen;
@@ -540,8 +540,6 @@ typedef struct shader_s {
float portalRange; // distance to fog out at
int multitextureEnv; // 0, GL_MODULATE, GL_ADD (FIXME: put in stage)
cullType_t cullType; // CT_FRONT_SIDED, CT_BACK_SIDED, or CT_TWO_SIDED
qboolean polygonOffset; // set for decals and other items that must be offset
qboolean noMipMaps; // for console fonts, 2D elements, etc.
@@ -829,7 +827,10 @@ typedef struct {
typedef union varnodeUnpacked_u {
float fVariance;
byte flags;
struct {
byte flags;
unsigned char unused[3];
} s;
} varnodeUnpacked_t;
typedef unsigned short terraInt;
@@ -893,7 +894,7 @@ typedef struct cTerraPatchUnpacked_s {
unsigned int uiDistRecalc;
float s;
float t;
float texCoord[2][2][2];
vec2_t texCoord[2][2];
float x0;
float y0;
float z0;
@@ -1069,9 +1070,7 @@ typedef struct {
const byte *vis; // may be passed in by CM_LoadMap to save space
byte *novis; // clusterBytes of 0xff
char *entityString;
char *entityParsePoint;
byte *lighting;
} world_t;
//======================================================================
@@ -1172,7 +1171,11 @@ typedef struct {
qboolean finishCalled;
int texEnv[2];
int faceCulling;
unsigned long glStateBits;
int cntTexEnvExt;
int cntnvblendmode;
long unsigned int glStateBits;
long unsigned int externalSetState;
vec4_t fFogColor;
} glstate_t;
@@ -1619,6 +1622,7 @@ void GL_Cull( int cullType );
#define GLS_DEPTHTEST_DISABLE 0x00000400
#define GLS_POLYMODE_LINE 0x00001000
#define GLS_MULTITEXTURE_ENV 0x00004000
#define GLS_DEPTHFUNC_EQUAL 0x00020000
@@ -1806,8 +1810,13 @@ WORLD MAP
============================================================
*/
extern terraTri_t* g_pTris;
extern terrainVert_t* g_pVert;
void R_AddBrushModelSurfaces( trRefEntity_t *e );
void R_GetInlineModelBounds(int iIndex, vec3_t vMins, vec3_t vMaxs);
int R_DlightTerrain(cTerraPatchUnpacked_t* surf, int dlightBits);
int R_CheckDlightTerrain(cTerraPatchUnpacked_t* surf, int dlightBits);
void R_AddWorldSurfaces( void );
qboolean R_inPVS( const vec3_t p1, const vec3_t p2 );
@@ -2013,6 +2022,7 @@ TERRAIN
=============================================================
*/
void R_MarkTerrainPatch(cTerraPatchUnpacked_t* pPatch);
void R_AddTerrainSurfaces();
void R_InitTerrain();
void R_TerrainPrepareFrame();
+58 -12
View File
@@ -354,6 +354,37 @@ static void DrawMultitextured( shaderCommands_t *input, int stage ) {
qglTexCoordPointer( 2, GL_FLOAT, 0, input->svars.texcoords[0] );
R_BindAnimatedImage( &pStage->bundle[0] );
if (pStage->stateBits & GLS_MULTITEXTURE_ENV)
{
glState.cntnvblendmode = pStage->multitextureEnv;
glState.cntTexEnvExt = GLS_MULTITEXTURE_ENV;
if (pStage->multitextureEnv == GL_ADD)
{
qglTexEnvf(8960, 34161, 260.0);
qglTexEnvf(8960, 34176, 33984.0);
qglTexEnvf(8960, 34192, 1145044992);
qglTexEnvf(8960, 34177, 0);
qglTexEnvf(8960, 34193, 1145061376);
qglTexEnvf(8960, 34178, 33985.0);
qglTexEnvf(8960, 34194, 1145044992);
qglTexEnvf(8960, 34179, 0);
qglTexEnvf(8960, 34195, 1145061376);
}
else if (pStage->multitextureEnv == GL_MODULATE)
{
qglTexEnvf(8960, 34161, 8448.0);
qglTexEnvf(8960, 34176, 33984.0);
qglTexEnvf(8960, 34192, 1145044992);
qglTexEnvf(8960, 34177, 33985.0);
qglTexEnvf(8960, 34193, 1145044992);
}
else
{
ri.Printf(3, "Unknown MT mode for: %s\n", input->shader);
}
}
//
// lightmap/secondary pass
//
@@ -361,23 +392,38 @@ static void DrawMultitextured( shaderCommands_t *input, int stage ) {
qglEnable( GL_TEXTURE_2D );
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
if ( r_lightmap->integer ) {
if ( r_lightmap->integer && pStage->bundle[1].isLightmap ) {
GL_TexEnv( GL_REPLACE );
} else if (pStage->stateBits & GLS_MULTITEXTURE_ENV) {
glState.cntTexEnvExt = GLS_MULTITEXTURE_ENV;
GL_TexEnv(GL_COMBINE);
qglTexEnvf(GL_TEXTURE_ENV, 34161, 34165.0);
qglTexEnvf(GL_TEXTURE_ENV, 34176, 0x47057700);
qglTexEnvf(GL_TEXTURE_ENV, 34192, 0x44400000);
qglTexEnvf(GL_TEXTURE_ENV, 34177, 34168.0);
qglTexEnvf(GL_TEXTURE_ENV, 34193, 0x44400000);
qglTexEnvf(GL_TEXTURE_ENV, 34178, 0x47057700);
qglTexEnvf(GL_TEXTURE_ENV, 34194, 770.0);
} else {
GL_TexEnv( tess.shader->multitextureEnv );
GL_TexEnv( pStage->multitextureEnv );
}
qglTexCoordPointer( 2, GL_FLOAT, 0, input->svars.texcoords[1] );
R_BindAnimatedImage( &pStage->bundle[1] );
if (!input->dlightMap || !pStage->bundle[1].isLightmap) {
R_BindAnimatedImage(&pStage->bundle[1]);
} else {
GL_Bind(&tr.identityLightImage[input->dlightMap]);
}
R_DrawElements( input->numIndexes, input->indexes );
qglDisable(GL_TEXTURE_2D);
//
// disable texturing on TEXTURE1, then select TEXTURE0
//
//qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
qglDisable( GL_TEXTURE_2D );
qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
GL_SelectTexture( 0 );
}
@@ -1158,21 +1204,21 @@ void RB_StageIteratorGeneric( void )
// to avoid compiling those arrays since they will change
// during multipass rendering
//
if ( tess.numPasses > 1 || input->shader->multitextureEnv )
if (tess.numPasses > 1 || (tess.xstages[0] && tess.xstages[0]->multitextureEnv))
{
setArraysOnce = qfalse;
qglDisableClientState (GL_COLOR_ARRAY);
qglDisableClientState (GL_TEXTURE_COORD_ARRAY);
qglDisableClientState(GL_COLOR_ARRAY);
qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
else
{
setArraysOnce = qtrue;
qglEnableClientState( GL_COLOR_ARRAY);
qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.svars.colors );
qglEnableClientState(GL_COLOR_ARRAY);
qglColorPointer(4, GL_UNSIGNED_BYTE, 0, tess.svars.colors);
qglEnableClientState( GL_TEXTURE_COORD_ARRAY);
qglTexCoordPointer( 2, GL_FLOAT, 0, tess.svars.texcoords[0] );
qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
qglTexCoordPointer(2, GL_FLOAT, 0, tess.svars.texcoords[0]);
}
//
+179 -145
View File
@@ -405,19 +405,19 @@ static void ParseWaveForm( char **text, waveForm_t *wave )
ParseTexMod
===================
*/
static void ParseTexMod( char *_text, shaderStage_t *stage )
static void ParseTexMod( char *_text, shaderStage_t *stage, int cntBundle )
{
const char *token;
char **text = &_text;
texModInfo_t *tmi;
if ( stage->bundle[0].numTexMods == TR_MAX_TEXMODS ) {
if ( stage->bundle[cntBundle].numTexMods == TR_MAX_TEXMODS ) {
ri.Error( ERR_DROP, "ERROR: too many tcMod unfoggedStages in shader '%s'\n", shader.name );
return;
}
tmi = &stage->bundle[0].texMods[stage->bundle[0].numTexMods];
stage->bundle[0].numTexMods++;
tmi = &stage->bundle[cntBundle].texMods[stage->bundle[cntBundle].numTexMods];
stage->bundle[cntBundle].numTexMods++;
token = COM_ParseExt( text, qfalse );
@@ -640,6 +640,7 @@ static qboolean ParseStage( shaderStage_t *stage, char **text )
char *token;
int depthMaskBits = GLS_DEPTHMASK_TRUE, blendSrcBits = 0, blendDstBits = 0, atestBits = 0, depthFuncBits = 0;
qboolean depthMaskExplicit = qfalse;
int cntBundle = 0;
stage->active = qtrue;
stage->noMipMaps = shader_noMipMaps;
@@ -673,23 +674,23 @@ static qboolean ParseStage( shaderStage_t *stage, char **text )
if ( !Q_stricmp( token, "$whiteimage" ) )
{
stage->bundle[0].image[0] = tr.whiteImage;
stage->bundle[cntBundle].image[0] = tr.whiteImage;
continue;
}
else if ( !Q_stricmp( token, "$lightmap" ) )
{
stage->bundle[0].isLightmap = qtrue;
stage->bundle[cntBundle].isLightmap = qtrue;
if ( shader.lightmapIndex < 0 ) {
stage->bundle[0].image[0] = tr.whiteImage;
stage->bundle[cntBundle].image[0] = tr.whiteImage;
} else {
stage->bundle[0].image[0] = tr.lightmaps[shader.lightmapIndex];
stage->bundle[cntBundle].image[0] = tr.lightmaps[shader.lightmapIndex];
}
continue;
}
else
{
stage->bundle[0].image[0] = R_FindImageFile( token, !stage->noMipMaps, !stage->noPicMip, qfalse, GL_REPEAT, GL_REPEAT );
if ( !stage->bundle[0].image[0] )
stage->bundle[cntBundle].image[0] = R_FindImageFile( token, !stage->noMipMaps, !stage->noPicMip, qfalse, GL_REPEAT, GL_REPEAT );
if ( !stage->bundle[cntBundle].image[0] )
{
ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name );
return qfalse;
@@ -708,8 +709,8 @@ static qboolean ParseStage( shaderStage_t *stage, char **text )
return qfalse;
}
stage->bundle[0].image[0] = R_FindImageFile(token, !stage->noMipMaps, !stage->noPicMip, qfalse, r_forceClampToEdge->value ? GL_CLAMP_TO_EDGE : GL_CLAMP, r_forceClampToEdge->value ? GL_CLAMP_TO_EDGE : GL_CLAMP);
if ( !stage->bundle[0].image[0] )
stage->bundle[cntBundle].image[0] = R_FindImageFile(token, !stage->noMipMaps, !stage->noPicMip, qfalse, r_forceClampToEdge->value ? GL_CLAMP_TO_EDGE : GL_CLAMP, r_forceClampToEdge->value ? GL_CLAMP_TO_EDGE : GL_CLAMP);
if ( !stage->bundle[cntBundle].image[0] )
{
ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name );
return qfalse;
@@ -726,7 +727,7 @@ static qboolean ParseStage( shaderStage_t *stage, char **text )
ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'animMmap' keyword in shader '%s'\n", shader.name );
return qfalse;
}
stage->bundle[0].imageAnimationSpeed = atof( token );
stage->bundle[cntBundle].imageAnimationSpeed = atof( token );
// parse up to MAX_IMAGE_ANIMATIONS animations
while ( 1 ) {
@@ -736,15 +737,15 @@ static qboolean ParseStage( shaderStage_t *stage, char **text )
if ( !token[0] ) {
break;
}
num = stage->bundle[0].numImageAnimations;
num = stage->bundle[cntBundle].numImageAnimations;
if ( num < MAX_IMAGE_ANIMATIONS ) {
stage->bundle[0].image[0] = R_FindImageFile(token, !stage->noMipMaps, !stage->noPicMip, qfalse, GL_REPEAT, GL_REPEAT );
if ( !stage->bundle[0].image[num] )
stage->bundle[cntBundle].image[0] = R_FindImageFile(token, !stage->noMipMaps, !stage->noPicMip, qfalse, GL_REPEAT, GL_REPEAT );
if ( !stage->bundle[cntBundle].image[num] )
{
ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name );
return qfalse;
}
stage->bundle[0].numImageAnimations++;
stage->bundle[cntBundle].numImageAnimations++;
}
}
}
@@ -1146,32 +1147,32 @@ static qboolean ParseStage( shaderStage_t *stage, char **text )
if ( !Q_stricmp( token, "environment" ) )
{
shader.needsNormal = qtrue;
stage->bundle[0].tcGen = TCGEN_ENVIRONMENT_MAPPED;
stage->bundle[cntBundle].tcGen = TCGEN_ENVIRONMENT_MAPPED;
}
else if (!Q_stricmp(token, "environmentmodel"))
{
shader.needsNormal = qtrue;
stage->bundle[0].tcGen = TCGEN_ENVIRONMENT_MAPPED2;
stage->bundle[cntBundle].tcGen = TCGEN_ENVIRONMENT_MAPPED2;
}
else if (!Q_stricmp(token, "sunreflection"))
{
shader.needsNormal = qtrue;
stage->bundle[0].tcGen = TCGEN_SUN_REFLECTION;
stage->bundle[cntBundle].tcGen = TCGEN_SUN_REFLECTION;
}
else if ( !Q_stricmp( token, "lightmap" ) )
{
stage->bundle[0].tcGen = TCGEN_LIGHTMAP;
stage->bundle[cntBundle].tcGen = TCGEN_LIGHTMAP;
}
else if ( !Q_stricmp( token, "texture" ) || !Q_stricmp( token, "base" ) )
{
stage->bundle[0].tcGen = TCGEN_TEXTURE;
stage->bundle[cntBundle].tcGen = TCGEN_TEXTURE;
}
else if ( !Q_stricmp( token, "vector" ) )
{
ParseVector( text, 3, stage->bundle[0].tcGenVectors[0] );
ParseVector( text, 3, stage->bundle[0].tcGenVectors[1] );
ParseVector( text, 3, stage->bundle[cntBundle].tcGenVectors[0] );
ParseVector( text, 3, stage->bundle[cntBundle].tcGenVectors[1] );
stage->bundle[0].tcGen = TCGEN_VECTOR;
stage->bundle[cntBundle].tcGen = TCGEN_VECTOR;
}
else
{
@@ -1194,7 +1195,7 @@ static qboolean ParseStage( shaderStage_t *stage, char **text )
strcat( buffer, " " );
}
ParseTexMod( buffer, stage );
ParseTexMod( buffer, stage, cntBundle );
continue;
}
@@ -1216,14 +1217,22 @@ static qboolean ParseStage( shaderStage_t *stage, char **text )
else if (!Q_stricmp(token, "nextBundle"))
{
if (!qglActiveTextureARB) {
continue;
ri.Printf(PRINT_ALL, "WARNING: " PRODUCT_NAME " requires a video card with multitexturing capability\n");
return qfalse;
}
token = COM_ParseExt(text, qfalse);
if (token[0] == 0) {
if (token[0] && !Q_stricmp(token, "add")) {
stage->multitextureEnv = GL_ADD;
} else {
stage->multitextureEnv = GL_MODULATE;
}
// FIXME: unimplemented
cntBundle++;
if (cntBundle > NUM_TEXTURE_BUNDLES) {
ri.Printf(PRINT_WARNING, "WARNING: too many nextBundle commands in shader '%s'\n", shader.name);
return qfalse;
}
continue;
}
else
@@ -1237,7 +1246,8 @@ static qboolean ParseStage( shaderStage_t *stage, char **text )
// if cgen isn't explicitly specified, use either identity or identitylighting
//
if ( stage->rgbGen == CGEN_BAD ) {
if ( blendSrcBits == 0 ||
if ( (!cntBundle && !stage->bundle[0].isLightmap) ||
blendSrcBits == 0 ||
blendSrcBits == GLS_SRCBLEND_ONE ||
blendSrcBits == GLS_SRCBLEND_SRC_ALPHA ) {
stage->rgbGen = CGEN_IDENTITY_LIGHTING;
@@ -1962,7 +1972,7 @@ static void ComputeStageIteratorFunc( void )
{
if ( !shader.polygonOffset )
{
if ( !shader.multitextureEnv )
if ( !unfoggedStages[0].multitextureEnv )
{
if ( !shader.numDeforms )
{
@@ -1990,7 +2000,7 @@ static void ComputeStageIteratorFunc( void )
{
if ( !shader.numDeforms )
{
if ( shader.multitextureEnv )
if (unfoggedStages[0].multitextureEnv )
{
shader.optimalStageIteratorFunc = RB_StageIteratorLightmappedMultitexture;
goto done;
@@ -2052,113 +2062,121 @@ Attempt to combine two unfoggedStages into a single multitexture stage
FIXME: I think modulated add + modulated add collapses incorrectly
=================
*/
static qboolean CollapseMultitexture( void ) {
static void CollapseMultitexture(int *stagecounter) {
int iUseCollapse;
int stagenum;
int abits, bbits;
int i;
shaderStage_t* stage;
textureBundle_t tmpBundle;
if ( !qglActiveTextureARB ) {
return qfalse;
}
for (stagenum = 0; stagenum < *stagecounter - 1; stagenum++) {
stage = &unfoggedStages[stagenum];
// make sure both unfoggedStages are active
if ( !unfoggedStages[0].active || !unfoggedStages[1].active ) {
return qfalse;
}
// on voodoo2, don't combine different tmus
if ( glConfig.driverType == GLDRV_VOODOO ) {
if ( unfoggedStages[0].bundle[0].image[0]->TMU ==
unfoggedStages[1].bundle[0].image[0]->TMU ) {
return qfalse;
// make sure both unfoggedStages are active
if (!stage->active || !unfoggedStages[stagenum + 1].active) {
continue;
}
}
abits = unfoggedStages[0].stateBits;
bbits = unfoggedStages[1].stateBits;
// make sure that both unfoggedStages have identical state other than blend modes
if ( ( abits & ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE ) ) !=
( bbits & ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE ) ) ) {
return qfalse;
}
abits &= ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS );
bbits &= ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS );
// search for a valid multitexture blend function
for ( i = 0; collapse[i].blendA != -1 ; i++ ) {
if ( abits == collapse[i].blendA
&& bbits == collapse[i].blendB ) {
break;
if (stage->multitextureEnv) {
continue;
}
}
// nothing found
if ( collapse[i].blendA == -1 ) {
return qfalse;
}
// on voodoo2, don't combine different tmus
if (glConfig.driverType == GLDRV_VOODOO) {
if (stage->bundle[0].image[0]->TMU ==
unfoggedStages[stagenum + 1].bundle[0].image[0]->TMU) {
continue;
}
}
// GL_ADD is a separate extension
if ( collapse[i].multitextureEnv == GL_ADD && !glConfig.textureEnvAddAvailable ) {
return qfalse;
}
abits = stage->stateBits;
bbits = unfoggedStages[stagenum + 1].stateBits;
// make sure waveforms have identical parameters
if ( ( unfoggedStages[0].rgbGen != unfoggedStages[1].rgbGen ) ||
( unfoggedStages[0].alphaGen != unfoggedStages[1].alphaGen ) ) {
return qfalse;
}
// make sure that both unfoggedStages have identical state other than blend modes
if ((abits & ~(GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE)) !=
(bbits & ~(GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE))) {
continue;
}
// an add collapse can only have identity colors
if ( collapse[i].multitextureEnv == GL_ADD && unfoggedStages[0].rgbGen != CGEN_IDENTITY ) {
return qfalse;
}
abits &= (GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS);
bbits &= (GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS);
if ( unfoggedStages[0].rgbGen == CGEN_WAVEFORM )
{
if ( memcmp( &unfoggedStages[0].rgbWave,
&unfoggedStages[1].rgbWave,
sizeof( unfoggedStages[0].rgbWave ) ) )
// search for a valid multitexture blend function
for (iUseCollapse = 0; collapse[iUseCollapse].blendA != -1; iUseCollapse++) {
if (abits == collapse[iUseCollapse].blendA
&& bbits == collapse[iUseCollapse].blendB) {
break;
}
}
// nothing found
if (collapse[iUseCollapse].blendA == -1) {
continue;
}
// GL_ADD is a separate extension
if (collapse[iUseCollapse].multitextureEnv == GL_ADD && !glConfig.textureEnvAddAvailable) {
continue;
}
// make sure waveforms have identical parameters
if ((stage->rgbGen != unfoggedStages[stagenum + 1].rgbGen) ||
(stage->alphaGen != unfoggedStages[stagenum + 1].alphaGen)) {
continue;
}
// an add collapse can only have identity colors
if (collapse[iUseCollapse].multitextureEnv == GL_ADD && stage->rgbGen != CGEN_IDENTITY) {
continue;
}
if (stage->rgbGen == CGEN_WAVEFORM)
{
return qfalse;
if (memcmp(&stage->rgbWave,
&unfoggedStages[stagenum + 1].rgbWave,
sizeof(stage->rgbWave)))
{
continue;
}
}
}
if ( unfoggedStages[0].alphaGen == CGEN_WAVEFORM )
{
if ( memcmp( &unfoggedStages[0].alphaWave,
&unfoggedStages[1].alphaWave,
sizeof( unfoggedStages[0].alphaWave ) ) )
if (stage->alphaGen == CGEN_WAVEFORM)
{
return qfalse;
if (memcmp(&stage->alphaWave,
&unfoggedStages[stagenum + 1].alphaWave,
sizeof(stage->alphaWave)))
{
continue;
}
}
// make sure that lightmaps are in bundle 1 for 3dfx
if (stage->bundle[0].isLightmap)
{
tmpBundle = stage->bundle[0];
stage->bundle[0] = unfoggedStages[stagenum + 1].bundle[0];
stage->bundle[1] = tmpBundle;
}
else
{
stage->bundle[1] = unfoggedStages[stagenum + 1].bundle[0];
}
// set the new blend state bits
stage->multitextureEnv = collapse[iUseCollapse].multitextureEnv;
stage->stateBits &= ~(GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS);
stage->stateBits |= collapse[iUseCollapse].multitextureBlend;
//
// move down subsequent shaders
//
if (stagenum + 2 < MAX_SHADER_STAGES) {
memmove(&unfoggedStages[stagenum + 1], &unfoggedStages[stagenum + 2], sizeof(unfoggedStages[0]) * (MAX_SHADER_STAGES - 2 - stagenum));
}
Com_Memset(&unfoggedStages[MAX_SHADER_STAGES - 1], 0, sizeof(unfoggedStages[0]));
(*stagecounter)--;
}
// make sure that lightmaps are in bundle 1 for 3dfx
if ( unfoggedStages[0].bundle[0].isLightmap )
{
tmpBundle = unfoggedStages[0].bundle[0];
unfoggedStages[0].bundle[0] = unfoggedStages[1].bundle[0];
unfoggedStages[0].bundle[1] = tmpBundle;
}
else
{
unfoggedStages[0].bundle[1] = unfoggedStages[1].bundle[0];
}
// set the new blend state bits
shader.multitextureEnv = collapse[i].multitextureEnv;
unfoggedStages[0].stateBits &= ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS );
unfoggedStages[0].stateBits |= collapse[i].multitextureBlend;
//
// move down subsequent shaders
//
memmove( &unfoggedStages[1], &unfoggedStages[2], sizeof( unfoggedStages[0] ) * ( MAX_SHADER_STAGES - 2 ) );
Com_Memset( &unfoggedStages[MAX_SHADER_STAGES-1], 0, sizeof( unfoggedStages[0] ) );
return qtrue;
}
/*
@@ -2425,6 +2443,7 @@ from the current global working shader
*/
static shader_t *FinishShader( void ) {
int stage;
int bundle;
qboolean hasLightmapStage;
qboolean vertexLightmap;
@@ -2478,25 +2497,34 @@ static shader_t *FinishShader( void ) {
//
// default texture coordinate generation
//
if ( pStage->bundle[0].isLightmap ) {
if ( pStage->bundle[0].tcGen == TCGEN_BAD ) {
pStage->bundle[0].tcGen = TCGEN_LIGHTMAP;
for (bundle = 2; bundle > 0; bundle--) {
if (pStage->bundle[bundle - 1].isLightmap) {
if (pStage->bundle[bundle - 1].tcGen == TCGEN_BAD) {
pStage->bundle[bundle - 1].tcGen = TCGEN_LIGHTMAP;
}
hasLightmapStage = qtrue;
}
hasLightmapStage = qtrue;
} else {
if ( pStage->bundle[0].tcGen == TCGEN_BAD ) {
pStage->bundle[0].tcGen = TCGEN_TEXTURE;
else {
if (pStage->bundle[bundle - 1].tcGen == TCGEN_BAD) {
pStage->bundle[bundle - 1].tcGen = TCGEN_TEXTURE;
}
}
}
// not a true lightmap but we want to leave existing
// behaviour in place and not print out a warning
//if (pStage->rgbGen == CGEN_VERTEX) {
// vertexLightmap = qtrue;
//}
// not a true lightmap but we want to leave existing
// behaviour in place and not print out a warning
//if (pStage->rgbGen == CGEN_VERTEX) {
// vertexLightmap = qtrue;
//}
if (pStage->multitextureEnv && pStage->bundle[0].isLightmap) {
//
// exchange bundle
//
textureBundle_t tmp = pStage->bundle[0];
pStage->bundle[0] = pStage->bundle[1];
pStage->bundle[1] = tmp;
}
//
// determine sort order and fog color adjustment
@@ -2537,7 +2565,7 @@ static shader_t *FinishShader( void ) {
// look for multitexture potential
//
if (stage > 1 && qglActiveTextureARB) {
CollapseMultitexture();
CollapseMultitexture(&stage);
};
// FIXME: fog shader
@@ -2874,6 +2902,8 @@ void R_ShaderList_f (void) {
count = 0;
for ( i = 0 ; i < tr.numShaders ; i++ ) {
int stage;
if ( ri.Cmd_Argc() > 1 ) {
shader = tr.sortedShaders[i];
} else {
@@ -2887,15 +2917,19 @@ void R_ShaderList_f (void) {
} else {
ri.Printf (PRINT_ALL, " ");
}
if ( shader->multitextureEnv == GL_ADD ) {
ri.Printf( PRINT_ALL, "MT(a) " );
} else if ( shader->multitextureEnv == GL_MODULATE ) {
ri.Printf( PRINT_ALL, "MT(m) " );
} else if ( shader->multitextureEnv == GL_DECAL ) {
ri.Printf( PRINT_ALL, "MT(d) " );
} else {
ri.Printf( PRINT_ALL, " " );
for (stage = 0; shader->unfoggedStages[stage] && shader->unfoggedStages[stage]->active; stage++) {
if (shader->unfoggedStages[stage]->multitextureEnv == GL_ADD ) {
ri.Printf( PRINT_ALL, "MT(a) " );
} else if (shader->unfoggedStages[stage]->multitextureEnv == GL_MODULATE ) {
ri.Printf( PRINT_ALL, "MT(m) " );
} else if (shader->unfoggedStages[stage]->multitextureEnv == GL_DECAL ) {
ri.Printf( PRINT_ALL, "MT(d) " );
} else {
ri.Printf( PRINT_ALL, " " );
}
}
if ( shader->explicitlyDefined ) {
ri.Printf( PRINT_ALL, "E " );
} else {
+73 -1
View File
@@ -718,7 +718,79 @@ void RB_SurfaceSkip( void *surf ) {
}
void RB_DrawTerrainTris(srfTerrain_t* p) {
// FIXME: unimplemented
int i;
terraInt numv;
int dlightBits;
if (tess.numVertexes + p->nVerts >= SHADER_MAX_VERTEXES || tess.numIndexes + p->nTris * 3 >= SHADER_MAX_INDEXES) {
RB_CheckOverflow(p->nVerts, 3 * p->nTris);
}
dlightBits = p->dlightBits[backEnd.smpFrame];
tess.dlightBits |= dlightBits;
if (p->dlightMap[backEnd.smpFrame])
{
float lmScale = (1 / 128.0) / p->lmapStep;
for (i = p->iVertHead; i; i = g_pVert[i].iNext) {
assert(tess.numVertexes < SHADER_MAX_VERTEXES);
VectorCopy(g_pVert[i].xyz, tess.xyz[tess.numVertexes]);
tess.texCoords[tess.numVertexes][0][0] = g_pVert[i].texCoords[0][0];
tess.texCoords[tess.numVertexes][0][1] = g_pVert[i].texCoords[0][1];
tess.texCoords[tess.numVertexes][1][0] = g_pVert[i].texCoords[1][0] * lmScale + p->lmapX;
tess.texCoords[tess.numVertexes][1][1] = g_pVert[i].texCoords[1][1] * lmScale + p->lmapY;
tess.normal[tess.numVertexes][0] = 0;
tess.normal[tess.numVertexes][1] = 0;
tess.normal[tess.numVertexes][2] = 1.0;
tess.vertexColors[tess.numVertexes][0] = -1;
tess.vertexColors[tess.numVertexes][1] = -1;
tess.vertexColors[tess.numVertexes][2] = -1;
tess.vertexColors[tess.numVertexes][3] = -1;
g_pVert[i].iVertArray = tess.numVertexes;
tess.numVertexes++;
}
}
else
{
for (i = p->iVertHead; i; i = g_pVert[i].iNext) {
assert(tess.numVertexes < SHADER_MAX_VERTEXES);
VectorCopy(g_pVert[i].xyz, tess.xyz[tess.numVertexes]);
tess.texCoords[tess.numVertexes][0][0] = g_pVert[i].texCoords[0][0];
tess.texCoords[tess.numVertexes][0][1] = g_pVert[i].texCoords[0][1];
tess.texCoords[tess.numVertexes][1][0] = g_pVert[i].texCoords[1][0];
tess.texCoords[tess.numVertexes][1][1] = g_pVert[i].texCoords[1][1];
tess.vertexDlightBits[tess.numVertexes] = dlightBits;
tess.normal[tess.numVertexes][0] = 0;
tess.normal[tess.numVertexes][1] = 0;
tess.normal[tess.numVertexes][2] = 1.0;
tess.vertexColors[tess.numVertexes][0] = -1;
tess.vertexColors[tess.numVertexes][1] = -1;
tess.vertexColors[tess.numVertexes][2] = -1;
tess.vertexColors[tess.numVertexes][3] = -1;
g_pVert[i].iVertArray = tess.numVertexes;
tess.numVertexes++;
}
}
for (i = p->iTriHead; i; i = g_pTris[i].iNext)
{
assert(tess.numVertexes < SHADER_MAX_INDEXES);
//
// Make sure these can be drawn
//
if (g_pTris[i].byConstChecks & 4)
{
tess.indexes[tess.numIndexes] = g_pVert[g_pTris[i].iPt[0]].iVertArray;
tess.indexes[tess.numIndexes + 1] = g_pVert[g_pTris[i].iPt[1]].iVertArray;
tess.indexes[tess.numIndexes + 2] = g_pVert[g_pTris[i].iPt[2]].iVertArray;
tess.numIndexes += 3;
}
}
}
void (*rb_surfaceTable[SF_NUM_SURFACE_TYPES])( void *) = {
+1366 -21
View File
File diff suppressed because it is too large. Load diff
+8 -1
View File
@@ -501,7 +501,14 @@ static void R_RecursiveWorldNode( mnode_t *node, int planeBits, int dlightBits )
}
}
// FIXME: terrain
if (r_drawterrain->integer)
{
int i;
for (i = 0; i < node->numTerraPatches; i++) {
R_MarkTerrainPatch(tr.world->visTerraPatches[node->firstTerraPatch + i]);
}
}
// FIXME: static decals
// FIXME: static models
}
+3 -3
View File
@@ -237,16 +237,16 @@ static char *XLateKey(XKeyEvent *ev, int *key)
case XK_Pause: *key = K_PAUSE; break;
case XK_Shift_L:
case XK_Shift_R: *key = K_SHIFT; break;
case XK_Shift_R: *key = K_LSHIFT; break;
case XK_Execute:
case XK_Control_L:
case XK_Control_R: *key = K_CTRL; break;
case XK_Control_R: *key = K_LCTRL; break;
case XK_Alt_L:
case XK_Meta_L:
case XK_Alt_R:
case XK_Meta_R: *key = K_ALT; break;
case XK_Meta_R: *key = K_LALT; break;
case XK_KP_Begin: *key = K_KP_5; break;