internal void DrawRectangle( game_offscreen_buffer *Buffer, v2 vMin, v2 vMax, real32 R, real32 G, real32 B ) { int32 MinX = RoundReal32ToInt32( vMin.X ); int32 MinY = RoundReal32ToInt32( vMin.Y ); int32 MaxX = RoundReal32ToInt32( vMax.X ); int32 MaxY = RoundReal32ToInt32( vMax.Y ); if( MinX < 0 ) MinX = 0; if( MinY < 0 ) MinY = 0; if ( MaxX > Buffer->Width ) MaxX = Buffer->Width; if ( MaxY > Buffer->Height ) MaxY = Buffer->Height; // BIT PATTERN: 0x AA RR GG BB uint32 Color = (RoundReal32ToUInt32( R * 255.0f ) << 16) | (RoundReal32ToUInt32( G * 255.0f ) << 8 ) | RoundReal32ToUInt32( B * 255.0f ); // Starting pointer. Top-left of rectangle uint8 *Row = (uint8*)Buffer->Memory + MinY * Buffer->Pitch + MinX * Buffer->BytesPerPixel; for ( int Y = MinY; Y < MaxY; ++Y ) { uint32 *Pixel = (uint32*)Row; for ( int X = MinX; X < MaxX; ++X ) { *Pixel++ = Color; } Row += Buffer->Pitch; } }
inline void RecanonicalizeCoord(tile_map *TileMap, uint32 *Tile, real32 *TileRel) { int32 Offset = RoundReal32ToInt32(*TileRel / TileMap->TileSideInMeters); *Tile += Offset; *TileRel -= (Offset)*TileMap->TileSideInMeters; }
internal void DrawRectangle(game_offscreen_buffer *Buffer, real32 RealMinX, real32 RealMinY, real32 RealMaxX, real32 RealMaxY, uint32 Color) { int32 MinX = (int32)(RoundReal32ToInt32(RealMinX)); int32 MinY = (int32)(RoundReal32ToInt32(RealMinY)); int32 MaxX = (int32)(RoundReal32ToInt32(RealMaxX)); int32 MaxY = (int32)(RoundReal32ToInt32(RealMaxY)); if(MinX < 0) { MinX = 0; } if(MinY < 0) { MinY = 0; } if(MaxX > Buffer->Width) { MaxX = Buffer->Width; } if(MaxY > Buffer->Height) { MaxY = Buffer->Height; } uint8 *Row = ((uint8 *)Buffer->Memory + MinX * Buffer->BytesPerPixel + MinY * Buffer->Pitch); for(int Y = MinY; Y < MaxY; ++Y) { uint32 *Pixel = (uint32 *)Row; for(int X = MinX; X < MaxX; ++X) { *Pixel++ = Color; } Row += Buffer->Pitch; } }
internal void DrawRectangle(game_offscreen_buffer* Buffer, real32 RealMinX, real32 RealMinY, real32 Width, real32 Height, real32 R, real32 G, real32 B) { int32 MinX = RoundReal32ToInt32(RealMinX); int32 MinY = RoundReal32ToInt32(RealMinY); int32 MaxX = RoundReal32ToInt32(RealMinX+Width); int32 MaxY = RoundReal32ToInt32(RealMinY+Height); // int32 iR =(int32) R*255; // int32 iG =(int32) G*255; // int32 iB =(int32) B*255; if(MinX < 0) { MinX = 0; } if(MinY < 0) { MinY = 0; } if(MaxX > Buffer->Width) { MaxX = Buffer->Width; } if(MaxY > Buffer->Height) { MaxY = Buffer->Height; } uint8* Row = ( (uint8*) Buffer->Memory + MinX * Buffer->BytesPerPixel + MinY * Buffer->Pitch); for(int Y = MinY; Y<MaxY; ++Y) { uint32* Pixel = (uint32*)Row; for(int X = MinX; X<MaxX; ++X) { *Pixel++ = ((TruncateReal32ToInt32(R*255.f) << 16) | (TruncateReal32ToInt32(G*255.f) << 8) | (TruncateReal32ToInt32(B*255.f) << 0)); } Row += Buffer->Pitch; } }
inline void RecanonicalizeCoord(TileMap* tileMap, uint32* tile, float* tileRel) { int32 offset = RoundReal32ToInt32(*tileRel / tileMap->tileSideInMeters); *tile += static_cast<uint32>(offset); *tileRel -= offset * tileMap->tileSideInMeters; ASSERT(*tileRel >= -0.5f * tileMap->tileSideInMeters); ASSERT(*tileRel <= 0.5f * tileMap->tileSideInMeters); }
internal void DrawBitmap( game_offscreen_buffer *Buffer, loaded_bitmap *Bitmap, real32 RealX, real32 RealY, real32 CAlpha = 1.0f) { int32 MinX = RoundReal32ToInt32( RealX ); int32 MinY = RoundReal32ToInt32( RealY ); int32 MaxX = MinX + Bitmap->Width; int32 MaxY = MinY + Bitmap->Height; int32 SourceOffsetX = 0; if ( MinX < 0 ) { SourceOffsetX = -MinX; MinX = 0; } int32 SourceOffsetY = 0; if ( MinY < 0 ) { SourceOffsetY = -MinY; MinY = 0; } if ( MaxX > Buffer->Width ) MaxX = Buffer->Width; if ( MaxY > Buffer->Height ) MaxY = Buffer->Height; uint32 *SourceRow = Bitmap->Pixels + Bitmap->Width * ( Bitmap->Height - 1 ); SourceRow += -Bitmap->Width * SourceOffsetY + SourceOffsetX; // top and left clipping uint8 *DestRow = (uint8*)Buffer->Memory + MinY * Buffer->Pitch + MinX * Buffer->BytesPerPixel; for ( int32 Y = MinY; Y < MaxY; ++Y ) { uint32 *Source = SourceRow; uint32 *Dest = (uint32*)DestRow; for ( int32 X = MinX; X < MaxX; ++X ) { real32 A = (real32)( ( *Source >> 24 ) & 0xFF ) / 255.0f; A *= CAlpha; real32 SR = (real32)( ( *Source >> 16 ) & 0xFF ); real32 SG = (real32)( ( *Source >> 8 ) & 0xFF ); real32 SB = (real32)( ( *Source >> 0 ) & 0xFF ); real32 DR = (real32)( ( *Dest >> 16 ) & 0xFF ); real32 DG = (real32)( ( *Dest >> 8 ) & 0xFF ); real32 DB = (real32)( ( *Dest >> 0 ) & 0xFF ); real32 R = ( 1.0f - A ) * DR + A * SR; real32 G = ( 1.0f - A ) * DG + A * SG; real32 B = ( 1.0f - A ) * DB + A * SB; // truncate *Dest = (((uint32)( R + 0.5f ) << 16 ) | ((uint32)( G + 0.5f ) << 8 ) | ((uint32)( B + 0.5f )) ); ++Dest; ++Source; } SourceRow -= Bitmap->Width; // bottom to top DestRow += Buffer->Pitch; } #if 0 // Clear screen DrawRectangle( Buffer, 0.0f, 0.0f, (real32)Buffer->Width, (real32)Buffer->Height, 1.0f, 0.0f, 1.0f ); #endif }
internal void MixSounds(SoundMixer *mixer, uint32 sampleFrequency, __m128 *realChannel0, __m128 *realChannel1, int32 sampleCount) { i32 sampleCount4 = sampleCount / 4; // simd globals __m128 zero = _mm_set1_ps(0.0f); __m128 one = _mm_set1_ps(1.0f); // get number of samples per second r32 secondsPerSample = 1.0f / (r32)sampleFrequency; // NOTE(Joey): clean channels with 0.0f before mixing for(int32 sampleIndex = 0; sampleIndex < sampleCount4; ++sampleIndex) { _mm_store_ps((float*)(realChannel0 + sampleIndex), zero); _mm_store_ps((float*)(realChannel1 + sampleIndex), zero); } // NOTE(Joey): loop over all playing sounds and mix into both real floating point channels for(PlayingSound **playingSoundPtr = &mixer->FirstPlayingSound; *playingSoundPtr; ) { PlayingSound *playingSound = *playingSoundPtr; bool32 soundFinished = false; Sound* sound = playingSound->Source; if(sound) { r32 volume0 = playingSound->CurrentVolume[0]; r32 volume1 = playingSound->CurrentVolume[1]; r32 dVolume0 = secondsPerSample*playingSound->dVolume[0]; r32 dVolume1 = secondsPerSample*playingSound->dVolume[1]; r32 dSample = playingSound->Pitch; __m128 *channel0 = realChannel0; __m128 *channel1 = realChannel1; __m128 masterVolume4 = _mm_set1_ps(mixer->MasterVolume); __m128 volume4_0 = _mm_setr_ps(volume0 + 0.0f*dVolume0, volume0 + 1.0f*dVolume0, volume0 + 2.0f*dVolume0, volume0 + 3.0f*dVolume0); __m128 volume4_1 = _mm_setr_ps(volume1 + 0.0f*dVolume1, volume1 + 1.0f*dVolume1, volume1 + 2.0f*dVolume1, volume1 + 3.0f*dVolume1); __m128 dVolume4_0 = _mm_set1_ps(4.0f*dVolume0); __m128 dVolume4_1 = _mm_set1_ps(4.0f*dVolume1); Assert(playingSound->SamplesPlayed >= 0); // NOTE(Joey): determine the maximum number of samples to mix this frame i32 nrSamplesToMix = sampleCount; // NOTE(Joey): deltaSampleRates can get negative (likely due to floating point precision) // this is accounted for in loop condition. i32 deltaSampleRates = (sound->SampleCount - RoundReal32ToInt32(playingSound->SamplesPlayed)); r32 realSamplesRemainingInSound = (r32)deltaSampleRates / (1.0f*dSample); i32 samplesRemainingInSound = RoundReal32ToInt32(realSamplesRemainingInSound); if(!playingSound->Loop && samplesRemainingInSound <= (i32)nrSamplesToMix) { nrSamplesToMix = samplesRemainingInSound; } // NOTE(Joey): determine if we need to break out of the loop early due to volume // attenuation reaching 0.0f volume per channel. bool32 volumeEnded[2] = {}; // TODO(Joey): make logic independent of nummer of channels // NOTE(Joey): could have a bug here where the volumeEnded (should) get triggered // for both volumes at the same time; which due to this logic will only work on one // volume item. I don't think this is a problem, but if so - this is likely it. if(dVolume0 != 0.0f) { r32 deltaVolume = playingSound->TargetVolume[0] - playingSound->CurrentVolume[0]; i32 volumeSampleCount = (i32)((deltaVolume / (1.0f*dVolume0)) + 0.5f); if(volumeSampleCount <= nrSamplesToMix) { nrSamplesToMix = volumeSampleCount; volumeEnded[0] = true; } } if(dVolume1 != 0.0f) { r32 deltaVolume = playingSound->TargetVolume[1] - playingSound->CurrentVolume[1]; i32 volumeSampleCount = (u32)((deltaVolume / (1.0f*dVolume1)) + 0.5f); if(volumeSampleCount <= nrSamplesToMix) { nrSamplesToMix = volumeSampleCount; volumeEnded[1] = true; } } // NOTE(Joey): we get into float precision issues; take expected begin/end sample // position and set playingSound->SamplePosition to expected end sample position. // then get next sample position in the loop loop index and start sample position. r32 beginSamplePosition = playingSound->SamplesPlayed; r32 endSamplePosition = beginSamplePosition + nrSamplesToMix*dSample; r32 loopIndexC = (endSamplePosition - beginSamplePosition) / (r32)nrSamplesToMix; // r32 samplePosition = playingSound->SamplesPlayed; // NOTE(Joey): clamp nrSamplesToMix loop condition to SIMD width of 4 for(i32 i = 0; i < nrSamplesToMix - (nrSamplesToMix & 3); i += 4) { r32 samplePosition = beginSamplePosition + loopIndexC*(r32)i; #if 0 // linear filtering // NOTE(Joey): disabled for now as it doesn't seem to make an 'audible' difference __m128 samplePos = _mm_setr_ps(samplePosition + 0.0f*dSample, samplePosition + 1.0f*dSample, samplePosition + 2.0f*dSample, samplePosition + 3.0f*dSample); __m128i sampleIndex = _mm_cvttps_epi32(samplePos); __m128 frac = _mm_sub_ps(samplePos, _mm_cvtepi32_ps(sampleIndex)); __m128 sampleValue0 = _mm_setr_ps(sound->Samples[0][((int32*)&sampleIndex)[0] % sound->SampleCount], sound->Samples[0][((int32*)&sampleIndex)[1] % sound->SampleCount], sound->Samples[0][((int32*)&sampleIndex)[2] % sound->SampleCount], sound->Samples[0][((int32*)&sampleIndex)[3] % sound->SampleCount]); __m128 sampleValue1 = _mm_setr_ps(sound->Samples[0][(((int32*)&sampleIndex)[0] + 1) % sound->SampleCount], sound->Samples[0][(((int32*)&sampleIndex)[1] + 1) % sound->SampleCount], sound->Samples[0][(((int32*)&sampleIndex)[2] + 1) % sound->SampleCount], sound->Samples[0][(((int32*)&sampleIndex)[3] + 1) % sound->SampleCount]); __m128 sampleValue = _mm_add_ps(_mm_mul_ps(_mm_sub_ps(one, frac), sampleValue0), _mm_mul_ps(frac, sampleValue1)); #else // nearest-neighbor filtering __m128i sampleIndex = _mm_setr_epi32(RoundReal32ToInt32(samplePosition + 0.0f*dSample) % sound->SampleCount, RoundReal32ToInt32(samplePosition + 1.0f*dSample) % sound->SampleCount, RoundReal32ToInt32(samplePosition + 2.0f*dSample) % sound->SampleCount, RoundReal32ToInt32(samplePosition + 3.0f*dSample) % sound->SampleCount); __m128 sampleValue = _mm_setr_ps(sound->Samples[0][((i32*)&sampleIndex)[0]], sound->Samples[0][((i32*)&sampleIndex)[1]], sound->Samples[0][((i32*)&sampleIndex)[2]], sound->Samples[0][((i32*)&sampleIndex)[3]]); #endif // NOTE(Joey): write 4 SIMD wide __m128 d0 = _mm_load_ps((float*)&channel0[0]); __m128 d1 = _mm_load_ps((float*)&channel1[0]); d0 = _mm_add_ps(d0, _mm_mul_ps(_mm_mul_ps(masterVolume4, volume4_0), sampleValue)); d1 = _mm_add_ps(d1, _mm_mul_ps(_mm_mul_ps(masterVolume4, volume4_1), sampleValue)); _mm_store_ps((float*)&channel0[0], d0); _mm_store_ps((float*)&channel1[0], d1); ++channel0; ++channel1; volume4_0 = _mm_add_ps(volume4_0, dVolume4_0); volume4_1 = _mm_add_ps(volume4_1, dVolume4_1); // samplePosition += 4.0f*dSample; } // playingSound->SamplesPlayed = samplePosition; playingSound->SamplesPlayed = endSamplePosition; playingSound->CurrentVolume[0] = ((real32*)&volume4_0)[0]; playingSound->CurrentVolume[1] = ((real32*)&volume4_1)[0]; // NOTE(Joey): if volume 0.0f is reached due to attenuation, reset delta volume for(int32 i = 0; i < ArrayCount(volumeEnded); ++i) { if(volumeEnded[i]) { playingSound->CurrentVolume[i] = playingSound->TargetVolume[i]; playingSound->dVolume[i] = 0.0f; } } // if loop, re-position SamplesPlayed to start of sound sample0 if(playingSound->Loop && playingSound->SamplesPlayed >= (r32)sound->SampleCount) playingSound->SamplesPlayed = playingSound->SamplesPlayed - (r32)sound->SampleCount; soundFinished = !playingSound->Loop && (uint32)playingSound->SamplesPlayed >= sound->SampleCount; if(soundFinished) dSample = 0.0f; } else { // NOTE(Joey): Load sound here? or when retrieving from Asset manager; I'd say load sound when // not available in asset manager, much better path to take } if(soundFinished) { *playingSoundPtr = playingSound->Next; playingSound->Next = mixer->FirstFreePlayingSound; mixer->FirstFreePlayingSound = playingSound; } else { playingSoundPtr = &playingSound->Next; } } }