diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b0bb0a1a0..080fcd287 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -64,7 +64,7 @@ jobs: -sdk macosx \ -configuration "Release (Default)" \ -destination generic/platform=macOS \ - GCC_PREPROCESSOR_DEFINITIONS='MISSING_SOUND_DATA $(GCC_PREPROCESSOR_DEFINITIONS)' \ + GCC_PREPROCESSOR_DEFINITIONS='$(GCC_PREPROCESSOR_DEFINITIONS)' \ -quiet \ clean archive # @NOTE: Newer versions of Xcode will complain and refuse to build old XIB files or target old iOS versions @@ -83,7 +83,7 @@ jobs: # -sdk iphoneos \ # -configuration "Release (iOS)" \ # -destination generic/platform=iOS \ - # GCC_PREPROCESSOR_DEFINITIONS='MISSING_SOUND_DATA $(GCC_PREPROCESSOR_DEFINITIONS)' \ + # GCC_PREPROCESSOR_DEFINITIONS='$(GCC_PREPROCESSOR_DEFINITIONS)' \ # -quiet \ # clean archive android: diff --git a/.gitignore b/.gitignore index 85fe04251..20855b3ec 100644 --- a/.gitignore +++ b/.gitignore @@ -211,6 +211,12 @@ xcuserdata/ /game/assets/gui/touchgui2.png /game/assets/item/arrows.png /game/assets/item/sign.png +# Sound +/game/assets/sound +/game/assets/newsound +/game/assets/sound3 +/game/assets/music +/game/assets/newmusic # Java /game/assets/terrain/sun.png /game/assets/terrain/moon.png @@ -242,11 +248,11 @@ xcuserdata/ # Ignore options.txt - where your configuration will be saved /game/options.txt /game/assetsO -/game/assets/gui/feedback_fill.png -/game/assets/gui/feedback_outer.png -/game/assets/snow.png -/game/assets/mob/pig.png # CLion /.idea -/cmake-build-* \ No newline at end of file +/cmake-build-* + +# PCM sound extraction +/tools/__pycache__ +/tools/venv diff --git a/README.md b/README.md index d72711ee5..db51a0e0f 100644 --- a/README.md +++ b/README.md @@ -68,12 +68,19 @@ This fetches the three dependencies we have: - [coi-serviceworker](https://github.com/gzuidhof/coi-serviceworker) - [gles-compatibility-layer](https://github.com/TheBrokenRail/gles-compatibility-layer.git) -2. Load the sound assets into the `sound_data/` folder in the root of the project -by **running the following command**: -* `tools/grabsounds.py /path/to/the/apk/lib/armeabi-v7a/libminecraftpe.so`. - -After that, **prepare the assets folder** from the apk. You will need it for the platform specific -build. +2. Copy the assets (including sounds and textures) into the necessary folders within the project.
+ Do this by performing **one** of the following: + - To extract all assets from _Pocket Edition_: + - **Run the following command**: + - `tools/extract_apk.py /path/to/the/apk` + - You can also run it with an interactive GUI by not providing arguments. + - To extract only the sounds from _Pocket Edition_: + - **Run the following command**: + - `tools/grabsounds.py /path/to/the/apk/lib/armeabi-v7a/libminecraftpe.so` + - You will need to extract textures and other assets manually. + - To retrieve only the sounds from _Java Edition_: + - Locate the `resources` directory in the `.minecraft` folder and copy its contents (e.g. `music`, `sound`, etc.) into the `game/assets/` directory of the project. + - You will need to extract textures and other assets manually from _Pocket Edition_. ## Building diff --git a/platforms/android/AppPlatform_android.cpp b/platforms/android/AppPlatform_android.cpp index 0f6fd4a4e..d8de4b86b 100644 --- a/platforms/android/AppPlatform_android.cpp +++ b/platforms/android/AppPlatform_android.cpp @@ -357,3 +357,22 @@ int AppPlatform_android::getKeyboardUpOffset() // For now we'll just return 1/2 of the screen height. That ought to cover most cases. return m_ScreenHeight / 2; } + +AssetFile AppPlatform_android::readAssetFile(const std::string& str, bool quiet) const +{ + std::string realPath = str; + if (realPath.size() && realPath[0] == '/') + // trim it off + realPath = realPath.substr(1); + + AAsset* asset = AAssetManager_open(m_app->activity->assetManager, str.c_str(), AASSET_MODE_BUFFER); + if (!asset) { + if (!quiet) LOG_E("File %s couldn't be opened", realPath.c_str()); + return AssetFile(); + } + size_t cnt = AAsset_getLength(asset); + unsigned char* buffer = new unsigned char[cnt]; + AAsset_read(asset, (void*)buffer, cnt); + AAsset_close(asset); + return AssetFile(ssize_t(cnt), buffer); +} \ No newline at end of file diff --git a/platforms/android/AppPlatform_android.hpp b/platforms/android/AppPlatform_android.hpp index 3e6de84f6..f7420a7c5 100644 --- a/platforms/android/AppPlatform_android.hpp +++ b/platforms/android/AppPlatform_android.hpp @@ -62,6 +62,8 @@ class AppPlatform_android : public AppPlatform void initAndroidApp(android_app* ptr); void setExternalStoragePath(const std::string& path); + AssetFile readAssetFile(const std::string&, bool) const override; + private: void changeKeyboardVisibility(bool bShown); diff --git a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj index c0dfd30a8..6bfc4cf21 100644 --- a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj +++ b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj @@ -200,6 +200,8 @@ 8426107E2AE989730065905F /* RakNetInstance.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 840DD64E2AC810620006A435 /* RakNetInstance.cpp */; }; 8426107F2AE989730065905F /* ServerSideNetworkHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 840DD6502AC810620006A435 /* ServerSideNetworkHandler.cpp */; }; 842610882AE98A4C0065905F /* libRakNet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FFBD7E2ACA2876005A8CCF /* libRakNet.a */; }; + 8435BB192DCD47F400D38282 /* SoundStreamAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8435BB182DCD47F400D38282 /* SoundStreamAL.cpp */; }; + 8435BB1A2DCD47F400D38282 /* SoundStreamAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8435BB182DCD47F400D38282 /* SoundStreamAL.cpp */; }; 8441F98F2DB4E911005977BD /* SoundSystemAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8441F9862DB4E911005977BD /* SoundSystemAL.cpp */; }; 8441F9962DB4E911005977BD /* SoundSystemAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8441F9862DB4E911005977BD /* SoundSystemAL.cpp */; }; 8443B6EA2A98675F0086730C /* libSDL2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8443B6E92A98675F0086730C /* libSDL2.a */; }; @@ -393,6 +395,11 @@ 849FF0DC2AF465340013BAE3 /* char.png in Resources */ = {isa = PBXBuildFile; fileRef = 84E0018E2AF3A28B009B9555 /* char.png */; }; 849FF0DD2AF465340013BAE3 /* particles.png in Resources */ = {isa = PBXBuildFile; fileRef = 84E0018F2AF3A28B009B9555 /* particles.png */; }; 849FF0DE2AF465340013BAE3 /* terrain.png in Resources */ = {isa = PBXBuildFile; fileRef = 84E001912AF3A28B009B9555 /* terrain.png */; }; + 84A2FF162DB5B7B70090CE3E /* AssetFile.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84A2FF152DB5B7B70090CE3E /* AssetFile.hpp */; }; + 84A2FF182DB61D440090CE3E /* SoundPathRepository.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84A2FF172DB61D3A0090CE3E /* SoundPathRepository.hpp */; }; + 84A2FF1A2DB61D720090CE3E /* SoundPathRepository.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84A2FF192DB61D6D0090CE3E /* SoundPathRepository.cpp */; }; + 84A2FF1C2DB61F650090CE3E /* SoundStream.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84A2FF1B2DB61F610090CE3E /* SoundStream.hpp */; }; + 84A2FF1E2DB61F6C0090CE3E /* SoundStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84A2FF1D2DB61F690090CE3E /* SoundStream.cpp */; }; 84AA8B502B32F39A003F5B82 /* BinaryHeap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84AA8B482B32F39A003F5B82 /* BinaryHeap.cpp */; }; 84AA8B512B32F39A003F5B82 /* BinaryHeap.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84AA8B492B32F39A003F5B82 /* BinaryHeap.hpp */; }; 84AA8B522B32F39A003F5B82 /* Node.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84AA8B4A2B32F39A003F5B82 /* Node.cpp */; }; @@ -1776,6 +1783,8 @@ 84336BA52B1EB57E00097DB0 /* Settings_iOS_Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Settings_iOS_Debug.xcconfig; path = ../Configuration/Settings_iOS_Debug.xcconfig; sourceTree = ""; }; 84336BA82B1EB88500097DB0 /* Settings_macOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Settings_macOS.xcconfig; path = ../Configuration/Settings_macOS.xcconfig; sourceTree = ""; }; 84336BA92B1EB9C200097DB0 /* Settings_macOS_Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Settings_macOS_Debug.xcconfig; path = ../Configuration/Settings_macOS_Debug.xcconfig; sourceTree = ""; }; + 8435BB172DCD47F400D38282 /* SoundStreamAL.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SoundStreamAL.hpp; sourceTree = ""; }; + 8435BB182DCD47F400D38282 /* SoundStreamAL.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SoundStreamAL.cpp; sourceTree = ""; }; 8441F9852DB4E911005977BD /* CustomSoundSystem.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CustomSoundSystem.hpp; sourceTree = ""; }; 8441F9862DB4E911005977BD /* SoundSystemAL.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SoundSystemAL.cpp; sourceTree = ""; }; 8443B6E92A98675F0086730C /* libSDL2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libSDL2.a; path = /opt/local/lib/libSDL2.a; sourceTree = ""; }; @@ -1925,6 +1934,11 @@ 849488352C9284DA006DB706 /* Lighting.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Lighting.hpp; sourceTree = ""; }; 8495E4872AF0905B00A06901 /* AppPlatform_iOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppPlatform_iOS.h; sourceTree = ""; }; 8495E4882AF0905B00A06901 /* AppPlatform_iOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AppPlatform_iOS.mm; sourceTree = ""; }; + 84A2FF152DB5B7B70090CE3E /* AssetFile.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AssetFile.hpp; sourceTree = ""; }; + 84A2FF172DB61D3A0090CE3E /* SoundPathRepository.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SoundPathRepository.hpp; sourceTree = ""; }; + 84A2FF192DB61D6D0090CE3E /* SoundPathRepository.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SoundPathRepository.cpp; sourceTree = ""; }; + 84A2FF1B2DB61F610090CE3E /* SoundStream.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SoundStream.hpp; sourceTree = ""; }; + 84A2FF1D2DB61F690090CE3E /* SoundStream.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SoundStream.cpp; sourceTree = ""; }; 84AA8B482B32F39A003F5B82 /* BinaryHeap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BinaryHeap.cpp; sourceTree = ""; }; 84AA8B492B32F39A003F5B82 /* BinaryHeap.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = BinaryHeap.hpp; sourceTree = ""; }; 84AA8B4A2B32F39A003F5B82 /* Node.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Node.cpp; sourceTree = ""; }; @@ -2314,14 +2328,15 @@ 840DD57D2AC810620006A435 /* app */ = { isa = PBXGroup; children = ( - 840DD57E2AC810620006A435 /* App.cpp */, 840DD57F2AC810620006A435 /* App.hpp */, - 840DD5802AC810620006A435 /* AppPlatform.cpp */, + 840DD57E2AC810620006A435 /* App.cpp */, 840DD5812AC810620006A435 /* AppPlatform.hpp */, - 840DD5822AC810620006A435 /* Minecraft.cpp */, + 840DD5802AC810620006A435 /* AppPlatform.cpp */, + 84A2FF152DB5B7B70090CE3E /* AssetFile.hpp */, 840DD5832AC810620006A435 /* Minecraft.hpp */, - 840DD5842AC810620006A435 /* NinecraftApp.cpp */, + 840DD5822AC810620006A435 /* Minecraft.cpp */, 840DD5852AC810620006A435 /* NinecraftApp.hpp */, + 840DD5842AC810620006A435 /* NinecraftApp.cpp */, ); path = app; sourceTree = ""; @@ -2531,15 +2546,19 @@ 840DD61A2AC810620006A435 /* sound */ = { isa = PBXGroup; children = ( - 840DD61B2AC810620006A435 /* SoundData.cpp */, + 84A2FF172DB61D3A0090CE3E /* SoundPathRepository.hpp */, + 84A2FF192DB61D6D0090CE3E /* SoundPathRepository.cpp */, 840DD61C2AC810620006A435 /* SoundData.hpp */, + 840DD61B2AC810620006A435 /* SoundData.cpp */, 840DD61D2AC810620006A435 /* SoundDefs.hpp */, - 840DD61E2AC810620006A435 /* SoundEngine.cpp */, 840DD61F2AC810620006A435 /* SoundEngine.hpp */, - 840DD6202AC810620006A435 /* SoundRepository.cpp */, + 840DD61E2AC810620006A435 /* SoundEngine.cpp */, 840DD6212AC810620006A435 /* SoundRepository.hpp */, - 840DD6222AC810620006A435 /* SoundSystem.cpp */, + 840DD6202AC810620006A435 /* SoundRepository.cpp */, + 84A2FF1B2DB61F610090CE3E /* SoundStream.hpp */, + 84A2FF1D2DB61F690090CE3E /* SoundStream.cpp */, 840DD6232AC810620006A435 /* SoundSystem.hpp */, + 840DD6222AC810620006A435 /* SoundSystem.cpp */, ); path = sound; sourceTree = ""; @@ -3383,6 +3402,8 @@ isa = PBXGroup; children = ( 8441F9852DB4E911005977BD /* CustomSoundSystem.hpp */, + 8435BB172DCD47F400D38282 /* SoundStreamAL.hpp */, + 8435BB182DCD47F400D38282 /* SoundStreamAL.cpp */, 8441F9862DB4E911005977BD /* SoundSystemAL.cpp */, ); path = openal; @@ -4081,9 +4102,11 @@ 84AA8C232B32F3F3003F5B82 /* ItemInHandRenderer.hpp in Headers */, 84AA8C272B32F3F3003F5B82 /* LevelRenderer.hpp in Headers */, 84AA8C292B32F3F3003F5B82 /* LightLayer.hpp in Headers */, + 84A2FF182DB61D440090CE3E /* SoundPathRepository.hpp in Headers */, 84AA8C2B2B32F3F3003F5B82 /* LightUpdate.hpp in Headers */, 84AA8C2D2B32F3F3003F5B82 /* PatchManager.hpp in Headers */, 84AA8C2F2B32F3F3003F5B82 /* RenderChunk.hpp in Headers */, + 84A2FF162DB5B7B70090CE3E /* AssetFile.hpp in Headers */, 84AA8C312B32F3F3003F5B82 /* RenderList.hpp in Headers */, 84AA8C332B32F3F3003F5B82 /* Tesselator.hpp in Headers */, 84AA8C342B32F3F3003F5B82 /* Texture.hpp in Headers */, @@ -4092,6 +4115,7 @@ 84AA8C392B32F3F3003F5B82 /* VertexPT.hpp in Headers */, 84AA8C5B2B32F535003F5B82 /* ChickenModel.hpp in Headers */, 84AA8C5D2B32F535003F5B82 /* CowModel.hpp in Headers */, + 84A2FF1C2DB61F650090CE3E /* SoundStream.hpp in Headers */, 84AA8C5F2B32F535003F5B82 /* CreeperModel.hpp in Headers */, 84AA8C672B32F535003F5B82 /* ModelPart.hpp in Headers */, 84AA8C692B32F535003F5B82 /* PigModel.hpp in Headers */, @@ -4856,6 +4880,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8435BB192DCD47F400D38282 /* SoundStreamAL.cpp in Sources */, 84EAE8F72AF1EAFA000894E8 /* main.cpp in Sources */, 841DD8772AF8AA7A00AA3B66 /* AppPlatform_sdl_base.cpp in Sources */, 841DD8782AF8AA7A00AA3B66 /* AppPlatform_sdl.cpp in Sources */, @@ -4877,6 +4902,7 @@ 8441F98F2DB4E911005977BD /* SoundSystemAL.cpp in Sources */, 84CEF0032AE3C97D006C5829 /* EAGLView.m in Sources */, 8495E4892AF0905B00A06901 /* AppPlatform_iOS.mm in Sources */, + 8435BB1A2DCD47F400D38282 /* SoundStreamAL.cpp in Sources */, 8488C08A2B1EDD4F001AEC4F /* ShowKeyboardView.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -4984,6 +5010,7 @@ 84AA8C182B32F3F3003F5B82 /* Font.cpp in Sources */, 84AA8C1A2B32F3F3003F5B82 /* Frustum.cpp in Sources */, 84AA8C1C2B32F3F3003F5B82 /* FrustumCuller.cpp in Sources */, + 84A2FF1A2DB61D720090CE3E /* SoundPathRepository.cpp in Sources */, 84AA8C1E2B32F3F3003F5B82 /* GameRenderer.cpp in Sources */, 84AA8C202B32F3F3003F5B82 /* GrassColor.cpp in Sources */, 84AA8C222B32F3F3003F5B82 /* ItemInHandRenderer.cpp in Sources */, @@ -4998,6 +5025,7 @@ 84AA8C302B32F3F3003F5B82 /* RenderList.cpp in Sources */, 84AA8C322B32F3F3003F5B82 /* Tesselator.cpp in Sources */, 84AA8C352B32F3F3003F5B82 /* Textures.cpp in Sources */, + 84A2FF1E2DB61F6C0090CE3E /* SoundStream.cpp in Sources */, 84AA8C372B32F3F3003F5B82 /* TileRenderer.cpp in Sources */, 84AA8C3A2B32F3F3003F5B82 /* WaterSideTexture.cpp in Sources */, 84AA8C3B2B32F3F3003F5B82 /* WaterTexture.cpp in Sources */, diff --git a/platforms/sdl/base/AppPlatform_sdl_base.cpp b/platforms/sdl/base/AppPlatform_sdl_base.cpp index aeeada144..dcbecc134 100644 --- a/platforms/sdl/base/AppPlatform_sdl_base.cpp +++ b/platforms/sdl/base/AppPlatform_sdl_base.cpp @@ -397,4 +397,31 @@ void AppPlatform_sdl_base::handleControllerAxisEvent(SDL_JoystickID controllerIn Controller::feedTrigger(2, val); break; } +} + +AssetFile AppPlatform_sdl_base::readAssetFile(const std::string& str, bool quiet) const +{ + std::string path = getAssetPath(str); + SDL_RWops *io = SDL_RWFromFile(path.c_str(), "rb"); + // Open File + if (!io) + { + if (!quiet) LOG_W("Couldn't find asset file: %s", path.c_str()); + return AssetFile(); + } + // Get File Size + int64_t size = SDL_RWsize(io); + if (size < 0) + { + if (!quiet) LOG_E("Error determining the size of the asset file!"); + SDL_RWclose(io); + return AssetFile(); + } + // Read Data + unsigned char *buf = new unsigned char[size]; + SDL_RWread(io, buf, size, 1); + // Close File + SDL_RWclose(io); + // Return + return AssetFile(size, buf); } \ No newline at end of file diff --git a/platforms/sdl/base/AppPlatform_sdl_base.hpp b/platforms/sdl/base/AppPlatform_sdl_base.hpp index 4e34e8285..7fe614cef 100644 --- a/platforms/sdl/base/AppPlatform_sdl_base.hpp +++ b/platforms/sdl/base/AppPlatform_sdl_base.hpp @@ -61,6 +61,9 @@ class AppPlatform_sdl_base : public AppPlatform void handleKeyEvent(int key, uint8_t state); void handleButtonEvent(SDL_JoystickID controllerIndex, uint8_t button, uint8_t state); void handleControllerAxisEvent(SDL_JoystickID controllerIndex, uint8_t axis, int16_t value); + + // Read Sounds + AssetFile readAssetFile(const std::string&, bool) const override; protected: SDL_Window *_window; private: diff --git a/platforms/sound/directsound/CustomSoundSystem.hpp b/platforms/sound/directsound/CustomSoundSystem.hpp index de50e210c..bac276636 100644 --- a/platforms/sound/directsound/CustomSoundSystem.hpp +++ b/platforms/sound/directsound/CustomSoundSystem.hpp @@ -29,15 +29,13 @@ class SoundSystemDS : public SoundSystem public: SoundSystemDS(); ~SoundSystemDS(); - virtual bool isAvailable(); - virtual void setListenerPos(float x, float y, float z); - virtual void setListenerAngle(float yaw, float pitch); - virtual void load(const std::string& sound); - virtual void play(const std::string& sound); - virtual void pause(const std::string& sound); - virtual void stop(const std::string& sound); - virtual void playAt(const SoundDesc& sound, float x, float y, float z, float a, float b); + virtual bool isAvailable() override; + virtual void setListenerPos(const Vec3& pos) override; + virtual void setListenerAngle(const Vec2& rot) override; + virtual void playAt(const SoundDesc& sound, const Vec3& pos, float volume, float pitch) override; private: + WAVEFORMATEX _getWaveFormat(const PCMSoundHeader& header, float pitch) const; + void _cleanSources(); struct BufferInfo { diff --git a/platforms/sound/directsound/SoundSystemDS.cpp b/platforms/sound/directsound/SoundSystemDS.cpp index a13a04241..26827c138 100644 --- a/platforms/sound/directsound/SoundSystemDS.cpp +++ b/platforms/sound/directsound/SoundSystemDS.cpp @@ -83,25 +83,25 @@ bool SoundSystemDS::isAvailable() return m_available; } -void SoundSystemDS::setListenerPos(float x, float y, float z) +void SoundSystemDS::setListenerPos(const Vec3& pos) { if (!isAvailable()) { return; } - m_listener->SetPosition(x, y, -z, DS3D_IMMEDIATE); + m_listener->SetPosition(pos.x, pos.y, -pos.z, DS3D_IMMEDIATE); } -void SoundSystemDS::setListenerAngle(float degyaw, float degpitch) +void SoundSystemDS::setListenerAngle(const Vec2& rot) { if (!isAvailable()) { return; } - float yaw = degyaw * M_PI / 180.f; - float pitch = degpitch * M_PI / 180.f; + float yaw = rot.x * M_PI / 180.f; + float pitch = rot.y * M_PI / 180.f; float lx = cosf(pitch) * sinf(yaw); float ly = -sinf(pitch); @@ -114,71 +114,29 @@ void SoundSystemDS::setListenerAngle(float degyaw, float degpitch) m_listener->SetOrientation(-lx,-ly,-lz, ux,uy,uz, DS3D_IMMEDIATE); } -void SoundSystemDS::load(const std::string& sound) +void SoundSystemDS::playAt(const SoundDesc& sound, const Vec3& pos, float volume, float pitch) { -} - -void SoundSystemDS::play(const std::string& sound) -{ -} - -void SoundSystemDS::pause(const std::string& sound) -{ -} - -void SoundSystemDS::stop(const std::string& sound) -{ -} - - -void SoundSystemDS::playAt(const SoundDesc& sound, float x, float y, float z, float volume, float pitch) -{ - //Directsound failed to initialize return to avoid crash. if (!isAvailable()) { + // DirectSound failed to initialize. Return to avoid crash. + assert(!"DirectSound is not initialized!"); return; } - //Release sounds that finished playing - for (size_t i = 0; i < m_buffers.size(); i++) - { - DWORD status; - m_buffers[i].buffer->GetStatus(&status); - if (status != DSBSTATUS_PLAYING) { - m_buffers[i].buffer->Release(); - if (m_buffers[i].object3d != NULL) - { - m_buffers[i].object3d->Release(); - } - m_buffers.erase(m_buffers.begin() + i); - i--; - } - } + // Release sounds that finished playing + _cleanSources(); HRESULT result; IDirectSoundBuffer* tempBuffer; unsigned char* bufferPtr; unsigned long bufferSize; - int length = sound.m_pHeader->m_length * sound.m_pHeader->m_bytes_per_sample; - bool is2D = sqrtf(x * x + y * y + z * z) == 0.f; - - //For some reason mojang made 3D sounds are REALLY quiet, with some of their volumes literally going below 0.1 - if (!is2D) - { - volume *= 5.f; - } + int length = sound.m_header.m_length * sound.m_header.m_bytes_per_sample; + bool is2D = pos.length() == 0.0f; LPDIRECTSOUNDBUFFER soundbuffer; //= (LPDIRECTSOUNDBUFFER*)calloc(1, sizeof(LPDIRECTSOUNDBUFFER)); - WAVEFORMATEX waveFormat; - waveFormat.wFormatTag = WAVE_FORMAT_PCM; - waveFormat.nSamplesPerSec = DWORD(float(sound.m_pHeader->m_sample_rate) * pitch); - waveFormat.wBitsPerSample = 8 * sound.m_pHeader->m_bytes_per_sample; - waveFormat.nChannels = sound.m_pHeader->m_channels; - waveFormat.nBlockAlign = (waveFormat.wBitsPerSample / 8) * waveFormat.nChannels; - waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; - waveFormat.cbSize = 0; + WAVEFORMATEX waveFormat = _getWaveFormat(sound.m_header, pitch); // Set the buffer description of the secondary sound buffer that the wave file will be loaded onto. DSBUFFERDESC bufferDesc; @@ -230,7 +188,7 @@ void SoundSystemDS::playAt(const SoundDesc& sound, float x, float y, float z, fl } //Move the wave data into the buffer. - memcpy(bufferPtr, sound.m_pData, length); + memcpy(bufferPtr, sound.m_buffer.m_pData, length); // Unlock the secondary buffer after the data has been written to it. result = soundbuffer->Unlock((void*)bufferPtr, bufferSize, NULL, 0); @@ -245,12 +203,8 @@ void SoundSystemDS::playAt(const SoundDesc& sound, float x, float y, float z, fl // https://learn.microsoft.com/en-us/previous-versions/windows/desktop/mt708939(v=vs.85) // Conversion from 0-1 linear volume to directsound logarithmic volume.. // This seems to work for the most part, but accuracy testing should be done for actual MCPE, water splashing is pretty quiet. - float attenuation = volume;//Lerp(DSBVOLUME_MIN, DSBVOLUME_MAX, volume); // clamp the attenuation value - if (attenuation < 0.0f) - attenuation = 0.0f; - else if (attenuation > 1.0f) - attenuation = 1.0f; + float attenuation = Mth::clamp(volume, 0.0f, 1.0f);//Lerp(DSBVOLUME_MIN, DSBVOLUME_MAX, volume); if (attenuation == 0) { @@ -267,8 +221,8 @@ void SoundSystemDS::playAt(const SoundDesc& sound, float x, float y, float z, fl info.buffer = soundbuffer; info.object3d = NULL; - //Check if position is not 0,0,0 and for mono to play 3D sound - if (!is2D && sound.m_pHeader->m_channels == 1) + //Check if position is not 0,0,0 to play 3D sound + if (!is2D) { LPDIRECTSOUND3DBUFFER object3d; @@ -279,11 +233,7 @@ void SoundSystemDS::playAt(const SoundDesc& sound, float x, float y, float z, fl return; } - object3d->SetPosition( - x, - y, - -z, - DS3D_IMMEDIATE); + object3d->SetPosition(pos.x, pos.y, -pos.z, DS3D_IMMEDIATE); //Im not really sure what values original MCPE would use. object3d->SetMinDistance(2.f, DS3D_IMMEDIATE); @@ -295,4 +245,40 @@ void SoundSystemDS::playAt(const SoundDesc& sound, float x, float y, float z, fl soundbuffer->Play(0, 0, 0); m_buffers.push_back(info); -} \ No newline at end of file +} + +WAVEFORMATEX SoundSystemDS::_getWaveFormat(const PCMSoundHeader& header, float pitch) const +{ + WAVEFORMATEX wf; + + wf.wFormatTag = WAVE_FORMAT_PCM; + wf.nSamplesPerSec = DWORD(float(header.m_sample_rate) * pitch); + wf.wBitsPerSample = 8 * header.m_bytes_per_sample; + wf.nChannels = header.m_channels; + wf.nBlockAlign = (wf.wBitsPerSample / 8) * wf.nChannels; + wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; + wf.cbSize = 0; + + return wf; +} + +// Release sounds that finished playing +void SoundSystemDS::_cleanSources() +{ + for (size_t i = 0; i < m_buffers.size(); i++) + { + DWORD status; + m_buffers[i].buffer->GetStatus(&status); + if (status != DSBSTATUS_PLAYING) + { + m_buffers[i].buffer->Release(); + if (m_buffers[i].object3d != NULL) + { + m_buffers[i].object3d->Release(); + } + + m_buffers.erase(m_buffers.begin() + i); + i--; + } + } +} diff --git a/platforms/sound/openal/CMakeLists.txt b/platforms/sound/openal/CMakeLists.txt index 101c05f28..881769405 100644 --- a/platforms/sound/openal/CMakeLists.txt +++ b/platforms/sound/openal/CMakeLists.txt @@ -4,6 +4,7 @@ project(reminecraftpe-openal) # Build add_library(reminecraftpe-sound STATIC SoundSystemAL.cpp + SoundStreamAL.cpp ) # Core diff --git a/platforms/sound/openal/CustomSoundSystem.hpp b/platforms/sound/openal/CustomSoundSystem.hpp index 639566914..c00e01d6f 100644 --- a/platforms/sound/openal/CustomSoundSystem.hpp +++ b/platforms/sound/openal/CustomSoundSystem.hpp @@ -1,18 +1,6 @@ #pragma once -#ifdef __APPLE__ -#include -#include -#elif defined(__EMSCRIPTEN__) -#include -#include -#else -#include "al.h" -#include "alc.h" -#ifdef _WIN32 -#pragma comment( lib, "OpenAL32.lib" ) -#endif -#endif +#include "thirdparty/OpenAL.h" #include #include @@ -20,8 +8,9 @@ #include "client/sound/SoundSystem.hpp" #include "world/phys/Vec3.hpp" -#define MAX_IDLE_SOURCES 50 -#define MAX_DISTANCE 16.0f +#include "SoundStreamAL.hpp" + +//#define SS_AL_SOURCES 12 // 0.10.0 #define SOUND_SYSTEM SoundSystemAL @@ -30,28 +19,47 @@ class SoundSystemAL : public SoundSystem public: SoundSystemAL(); ~SoundSystemAL(); - virtual bool isAvailable(); - void update(); - virtual void playAt(const SoundDesc& sound, float x, float y, float z, float volume, float pitch); + virtual bool isAvailable() override; + virtual void setListenerPos(const Vec3& pos) override; + virtual void setListenerAngle(const Vec2& rot) override; + + virtual void setMusicVolume(float vol) override; + + virtual void playAt(const SoundDesc& sound, const Vec3& pos, float volume, float pitch) override; - virtual void setListenerPos(float x, float y, float z); - virtual void setListenerAngle(float yaw, float pitch); + virtual void playMusic(const std::string& soundPath) override; + virtual bool isPlayingMusic() const override; + virtual void stopMusic() override; + virtual void pauseMusic(bool state) override; - virtual void startEngine(); - virtual void stopEngine(); + virtual void update(float elapsedTime) override; + + virtual void startEngine() override; + virtual void stopEngine() override; private: - void delete_sources(); - void delete_buffers(); - ALuint get_buffer(const SoundDesc& sound); + bool _hasMaxSources() const; + ALuint _getIdleSource(); + ALuint _getSource(bool& isNew, bool tryClean = true); + void _deleteSources(); + void _cleanSources(); + ALuint _getBuffer(const SoundDesc& sound); + void _deleteBuffers(); + ALenum _getSoundFormat(const PCMSoundHeader& header) const; ALCdevice *_device; ALCcontext *_context; - bool _initialized; + + //ALuint m_sources[SS_AL_SOURCES]; // 0.10.0 std::vector _sources; std::vector _sources_idle; - std::map _buffers; + std::map _buffers; + SoundStreamAL* _musicStream; + + bool _initialized; + + float _mainVolume; - Vec3 _lastListenerPos; - float _listenerVolume; + Vec3 _listenerPos; + float _listenerYaw; }; diff --git a/platforms/sound/openal/SoundStreamAL.cpp b/platforms/sound/openal/SoundStreamAL.cpp new file mode 100644 index 000000000..b6848e0c5 --- /dev/null +++ b/platforms/sound/openal/SoundStreamAL.cpp @@ -0,0 +1,156 @@ +#include "SoundStreamAL.hpp" +#include +#include "common/Logger.hpp" + +SoundStreamAL::SoundStreamAL() + : SoundStream() +{ + _createSource(); + _createBuffers(); + _alFormat = AL_NONE; + + _setVolume(getVolume()); +} + +SoundStreamAL::~SoundStreamAL() +{ + _deleteSource(); + _deleteBuffers(); +} + +void SoundStreamAL::_deleteSource() +{ + alDeleteSources(1, &_source); + AL_ERROR_CHECK(); +} + +void SoundStreamAL::_createSource() +{ + alGenSources(1, &_source); +} + +void SoundStreamAL::_resetSource() +{ + alSourceStop(_source); + AL_ERROR_CHECK(); + alSourceRewind(_source); + AL_ERROR_CHECK(); + // Detach all of the buffers from the source + alSourcei(_source, AL_BUFFER, AL_NONE); + AL_ERROR_CHECK(); +} + +void SoundStreamAL::_deleteBuffers() +{ + alDeleteBuffers(_buffers.size(), _buffers.data()); + AL_ERROR_CHECK(); + + _buffers.clear(); + _bufferIdMap.clear(); +} + +void SoundStreamAL::_createBuffers() +{ + // Should not be called while buffers are already present + assert(_buffers.size() == 0); + + ALuint buffers[2]; + alGenBuffers(2, buffers); + for (int i = 0; i < sizeof(buffers) / sizeof(ALuint); i++) + { + _buffers.push_back(buffers[i]); + _bufferIdMap.insert(std::make_pair(buffers[i], i )); + } +} + +void SoundStreamAL::_resetBuffers() +{ + _deleteBuffers(); + _createBuffers(); +} + +void SoundStreamAL::_setVolume(float vol) +{ + alSourcef(_source, AL_GAIN, vol); + AL_ERROR_CHECK(); +} + +void SoundStreamAL::_play() +{ + alSourcePlay(_source); + AL_ERROR_CHECK(); +} + +void SoundStreamAL::_pause() +{ + alSourcePause(_source); + AL_ERROR_CHECK(); +} + +bool SoundStreamAL::_open(const std::string& fileName) +{ + if (m_info.channels == 2) _alFormat = AL_FORMAT_STEREO16; + else _alFormat = AL_FORMAT_MONO16; + + size_t size = _buffers.size(); + for (int i = 0; i < size; i++) + { + if (!_stream(i)) return false; + } + alSourceQueueBuffers(_source, size, _buffers.data()); + AL_ERROR_CHECK(); + _play(); + + return true; +} + +void SoundStreamAL::_close() +{ + _resetSource(); + //_resetBuffers(); + _alFormat = AL_NONE; +} + +void SoundStreamAL::_update() +{ + ALint processed = 0; + + alGetSourcei(_source, AL_BUFFERS_PROCESSED, &processed); + AL_ERROR_CHECK(); + + while (processed--) + { + ALuint buffer = 0; + + alSourceUnqueueBuffers(_source, 1, &buffer); + AL_ERROR_CHECK(); + + int bufferId = _bufferIdMap[buffer]; + + if (!_stream(bufferId)) + { + bool shouldExit = true; + + if (shouldLoop()) + { + stb_vorbis_seek_start(m_decoder); + m_totalSamplesLeft = stb_vorbis_stream_length_in_samples(m_decoder) * m_info.channels; + shouldExit = !_stream(bufferId); + } + + if (shouldExit) + { + close(); + return; + } + } + alSourceQueueBuffers(_source, 1, &buffer); + AL_ERROR_CHECK(); + } +} + +void SoundStreamAL::_publishBuffer(unsigned int destBufferId, const SoundBuffer& sourceBuffer) +{ + alBufferData(_buffers[destBufferId], _alFormat, sourceBuffer.m_pData, sourceBuffer.m_dataSize * sizeof(int16_t), m_info.sample_rate); + AL_ERROR_CHECK(); +} \ No newline at end of file diff --git a/platforms/sound/openal/SoundStreamAL.hpp b/platforms/sound/openal/SoundStreamAL.hpp new file mode 100644 index 000000000..287b8b2c6 --- /dev/null +++ b/platforms/sound/openal/SoundStreamAL.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include + +#include "thirdparty/OpenAL.h" + +#include "client/sound/SoundStream.hpp" + +class SoundStreamAL : public SoundStream +{ +private: + typedef std::map BufferIdMap; + +private: + std::vector _buffers; + BufferIdMap _bufferIdMap; + + ALuint _source; + + ALenum _alFormat; + +public: + SoundStreamAL(); + ~SoundStreamAL(); + +private: + void _deleteSource(); + void _createSource(); + void _resetSource(); + + void _deleteBuffers(); + void _createBuffers(); + void _resetBuffers(); + +protected: + void _setVolume(float vol) override; + void _play() override; + void _pause() override; + bool _open(const std::string& fileName) override; + void _close() override; + void _update() override; + void _publishBuffer(unsigned int destBufferId, const SoundBuffer& sourceBuffer); +}; \ No newline at end of file diff --git a/platforms/sound/openal/SoundSystemAL.cpp b/platforms/sound/openal/SoundSystemAL.cpp index 21c48688f..56e134bf6 100644 --- a/platforms/sound/openal/SoundSystemAL.cpp +++ b/platforms/sound/openal/SoundSystemAL.cpp @@ -2,10 +2,17 @@ #include "common/Utils.hpp" +#include "SoundStreamAL.hpp" + SoundSystemAL::SoundSystemAL() { + _device = nullptr; + _context = nullptr; _initialized = false; - _listenerVolume = 1.0; + _musicStream = nullptr; + _mainVolume = 1.0f; + _listenerPos = Vec3::ZERO; + _listenerYaw = 0.0f; startEngine(); } @@ -15,20 +22,72 @@ SoundSystemAL::~SoundSystemAL() stopEngine(); } -// Error Checking -#define AL_ERROR_CHECK() AL_ERROR_CHECK_MANUAL(alGetError()) -#define AL_ERROR_CHECK_MANUAL(val) \ - { \ - ALenum __err = val; \ - if (__err != AL_NO_ERROR) \ - { \ - LOG_E("(%s:%i) OpenAL Error: %s", __FILE__, __LINE__, alGetString(__err)); \ - assert(!"An OpenAL error occurred!"); \ - } \ +bool SoundSystemAL::_hasMaxSources() const +{ + return _sources.size() + _sources_idle.size() >= SOUND_MAX_SOURCES; +} + +ALuint SoundSystemAL::_getIdleSource() +{ + ALuint al_source = AL_NONE; + + if (_sources_idle.size() > 0) + { + // Use Idle Source + al_source = _sources_idle.back(); + _sources_idle.pop_back(); + } + + return al_source; +} + +ALuint SoundSystemAL::_getSource(bool& isNew, bool tryClean) +{ + isNew = false; + ALuint al_source = _getIdleSource(); // Try to fetch pre-existing idle source + if (!al_source) + { + // Could not find pre-existing idle source + + if (_hasMaxSources()) + { + if (tryClean) + { + // Clean finished sources since no idle ones are available + _cleanSources(); + + // Did some cleaning, lets try again... + return _getSource(isNew, false); + } + else + { + // Too many sources already exist and they're all in-use, sucks to suck... + return AL_NONE; + } + } + + // Create Source + alGenSources(1, &al_source); + // Special Out-Of-Memory Handling + { + ALenum err = alGetError(); + if (err == AL_OUT_OF_MEMORY) + { + return AL_NONE; + } + else + { + AL_ERROR_CHECK_MANUAL(err); + } + } + isNew = true; } + return al_source; +} + // Delete Sources -void SoundSystemAL::delete_sources() +void SoundSystemAL::_deleteSources() { if (_initialized) { @@ -47,8 +106,75 @@ void SoundSystemAL::delete_sources() _sources.clear(); } +// Clear Finished Sources +void SoundSystemAL::_cleanSources() +{ + std::vector::iterator it = _sources.begin(); + while (it != _sources.end()) + { + ALuint source = *it; + bool remove = false; + // Check + if (source && alIsSource(source)) + { + // Is Valid Source + ALint source_state; + alGetSourcei(source, AL_SOURCE_STATE, &source_state); + AL_ERROR_CHECK(); + if (source_state != AL_PLAYING) + { + // Finished Playing + remove = true; + _sources_idle.push_back(source); + + // Reset playback state of source to prevent buffer ghosting on legacy Mac OS X and Windows. + // see: https://stackoverflow.com/questions/6960731/openal-problem-changing-gain-of-source + alSourceStop(source); + AL_ERROR_CHECK(); + alSourceRewind(source); + AL_ERROR_CHECK(); + } + } + else + { + // Not A Source + remove = true; + } + // Remove If Needed + if (remove) + { + it = _sources.erase(it); + } + else + { + ++it; + } + } +} + +// Get Buffer +ALuint SoundSystemAL::_getBuffer(const SoundDesc& sound) +{ + // Fetch pre-existing buffer + if (_buffers.count(sound.m_buffer.m_pData) > 0) + { + return _buffers[sound.m_buffer.m_pData]; + } + + // Create Buffer + ALuint buffer; + alGenBuffers(1, &buffer); + AL_ERROR_CHECK(); + alBufferData(buffer, _getSoundFormat(sound.m_header), sound.m_buffer.m_pData, sound.m_buffer.m_dataSize, sound.m_header.m_sample_rate); + AL_ERROR_CHECK(); + + // Store + _buffers[sound.m_buffer.m_pData] = buffer; + return buffer; +} + // Delete Buffers -void SoundSystemAL::delete_buffers() +void SoundSystemAL::_deleteBuffers() { if (_initialized) { @@ -65,39 +191,16 @@ void SoundSystemAL::delete_buffers() _buffers.clear(); } -// Get Buffer -ALuint SoundSystemAL::get_buffer(const SoundDesc& sound) +ALenum SoundSystemAL::_getSoundFormat(const PCMSoundHeader& header) const { - if (_buffers.count(sound.m_pData) > 0) - { - return _buffers[sound.m_pData]; - } - else + switch (header.m_channels) { - // Sound Format - ALenum format = AL_NONE; - if (sound.m_header.m_channels == 1) - { - format = sound.m_header.m_bytes_per_sample == 2 ? AL_FORMAT_MONO16 : AL_FORMAT_MONO8; - } - else if (sound.m_header.m_channels == 2) - { - format = sound.m_header.m_bytes_per_sample == 2 ? AL_FORMAT_STEREO16 : AL_FORMAT_STEREO8; - } - - // Sound Data Size - int size = sound.m_header.m_channels * sound.m_header.m_length * sound.m_header.m_bytes_per_sample; - - // Create Buffer - ALuint buffer; - alGenBuffers(1, &buffer); - AL_ERROR_CHECK(); - alBufferData(buffer, format, sound.m_pData, size, sound.m_header.m_sample_rate); - AL_ERROR_CHECK(); - - // Store - _buffers[sound.m_pData] = buffer; - return buffer; + case 1: + return header.m_bytes_per_sample == 2 ? AL_FORMAT_MONO16 : AL_FORMAT_MONO8; + case 2: + return header.m_bytes_per_sample == 2 ? AL_FORMAT_STEREO16 : AL_FORMAT_STEREO8; + default: + return AL_NONE; } } @@ -106,81 +209,43 @@ bool SoundSystemAL::isAvailable() return _initialized; } -void SoundSystemAL::setListenerPos(float x, float y, float z) +void SoundSystemAL::setListenerPos(const Vec3& pos) { + // Empty on iOS 0.10.0 + if (_listenerPos == pos) + return; // No need to waste time doing math and talking to OpenAL + // Update Listener Position - alListener3f(AL_POSITION, x, y, z); + alListener3f(AL_POSITION, pos.x, pos.y, pos.z); + AL_ERROR_CHECK(); + _listenerPos = pos; + + // Update Listener Volume + alListenerf(AL_GAIN, _mainVolume); AL_ERROR_CHECK(); - _lastListenerPos = Vec3(x, y, z); - update(); } -void SoundSystemAL::setListenerAngle(float yaw, float pitch) +void SoundSystemAL::setListenerAngle(const Vec2& rot) { + if (_listenerYaw == rot.x) + return; // No need to waste time doing math and talking to OpenAL + // Update Listener Orientation - float radian_yaw = yaw * (M_PI / 180); + float radian_yaw = rot.x * (M_PI / 180); ALfloat orientation[] = { -sinf(radian_yaw), 0.0f, cosf(radian_yaw), 0.0f, 1.0f, 0.0f }; alListenerfv(AL_ORIENTATION, orientation); AL_ERROR_CHECK(); + _listenerYaw = rot.x; } -void SoundSystemAL::update() +void SoundSystemAL::setMusicVolume(float vol) { - // Check - if (!_initialized) - { - return; - } - - // Update Listener Volume - alListenerf(AL_GAIN, _listenerVolume); - AL_ERROR_CHECK(); + assert(_musicStream != nullptr); - // Clear Finished Sources - std::vector::iterator it = _sources.begin(); - while (it != _sources.end()) - { - ALuint source = *it; - bool remove = false; - // Check - if (source && alIsSource(source)) - { - // Is Valid Source - ALint source_state; - alGetSourcei(source, AL_SOURCE_STATE, &source_state); - AL_ERROR_CHECK(); - if (source_state != AL_PLAYING) - { - // Finished Playing - remove = true; - if (_sources_idle.size() < MAX_IDLE_SOURCES) - { - _sources_idle.push_back(source); - } - else - { - alDeleteSources(1, &source); - AL_ERROR_CHECK(); - } - } - } - else - { - // Not A Source - remove = true; - } - // Remove If Needed - if (remove) { - it = _sources.erase(it); - } - else - { - ++it; - } - } + _musicStream->setVolume(vol); } -void SoundSystemAL::playAt(const SoundDesc& sound, float x, float y, float z, float volume, float pitch) +void SoundSystemAL::playAt(const SoundDesc& sound, const Vec3& pos, float volume, float pitch) { // Check if (!_initialized) @@ -193,71 +258,57 @@ void SoundSystemAL::playAt(const SoundDesc& sound, float x, float y, float z, fl bool bIsGUI = AL_FALSE; float distance = 0.0f; - if (x == 0 && y == 0 && z == 0) + if (pos == Vec3::ZERO) { bIsGUI = AL_TRUE; } - else - { - distance = Vec3(x, y, z).distanceTo(_lastListenerPos); - if (distance >= MAX_DISTANCE) - return; - } // Load Sound - ALuint buffer = get_buffer(sound); + ALuint buffer = _getBuffer(sound); if (!buffer) return; - + // Get Source - ALuint al_source; - if (_sources_idle.size() > 0) + bool isNew; + ALuint al_source = _getSource(isNew); + if (!al_source) { - // Use Idle Source - al_source = _sources_idle.back(); - _sources_idle.pop_back(); - } - else - { - // Create Source - alGenSources(1, &al_source); - // Special Out-Of-Memory Handling - { - ALenum err = alGetError(); - if (err == AL_OUT_OF_MEMORY) - { - return; - } - else - { - AL_ERROR_CHECK_MANUAL(err); - } - } + // Couldn't get a source, just give up. + return; } // Set Properties alSourcef(al_source, AL_PITCH, pitch); AL_ERROR_CHECK(); - // There is a problem with Apple's OpenAL implementation on older Mac OS X versions - // https://stackoverflow.com/questions/6960731/openal-problem-changing-gain-of-source alSourcef(al_source, AL_GAIN, volume); AL_ERROR_CHECK(); - alSource3f(al_source, AL_POSITION, x, y, z); - AL_ERROR_CHECK(); - alSource3f(al_source, AL_VELOCITY, 0, 0, 0); - AL_ERROR_CHECK(); - alSourcei(al_source, AL_LOOPING, AL_FALSE); + alSource3f(al_source, AL_POSITION, pos.x, pos.y, pos.z); AL_ERROR_CHECK(); alSourcei(al_source, AL_SOURCE_RELATIVE, bIsGUI); AL_ERROR_CHECK(); - // Set Attenuation - alSourcef(al_source, AL_MAX_DISTANCE, MAX_DISTANCE); - AL_ERROR_CHECK(); - alSourcef(al_source, AL_ROLLOFF_FACTOR, 1.0f); - AL_ERROR_CHECK(); - alSourcef(al_source, AL_REFERENCE_DISTANCE, 5.0f); - AL_ERROR_CHECK(); + // Only set constant parameters if source isn't reused + if (isNew) + { + // Set Attenuation + alSourcef(al_source, AL_MAX_DISTANCE, SOUND_MAX_DISTANCE); + AL_ERROR_CHECK(); + alSourcef(al_source, AL_ROLLOFF_FACTOR, 0.9f); // 0.9f is audibly on-par with b1.2_02's rolloff factor. So you probably shouldn't change it. 0.03f is default value for Paulscode. + AL_ERROR_CHECK(); + alSourcef(al_source, AL_REFERENCE_DISTANCE, 5.0f); // Sounds the same regardless of being set. Paulscode doesn't set this. + AL_ERROR_CHECK(); + + alSource3f(al_source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); + AL_ERROR_CHECK(); + alSourcei(al_source, AL_LOOPING, AL_FALSE); + AL_ERROR_CHECK(); + } + else + { + // Detach all of the buffers from the source + alSourcei(al_source, AL_BUFFER, AL_NONE); + AL_ERROR_CHECK(); + } // Set Buffer alSourcei(al_source, AL_BUFFER, buffer); @@ -269,6 +320,31 @@ void SoundSystemAL::playAt(const SoundDesc& sound, float x, float y, float z, fl _sources.push_back(al_source); } +void SoundSystemAL::playMusic(const std::string& soundPath) +{ + _musicStream->open(soundPath); +} + +bool SoundSystemAL::isPlayingMusic() const +{ + return _musicStream->isPlaying(); +} + +void SoundSystemAL::stopMusic() +{ + _musicStream->close(); +} + +void SoundSystemAL::pauseMusic(bool state) +{ + _musicStream->setPausedState(state); +} + +void SoundSystemAL::update(float elapsedTime) +{ + _musicStream->update(); +} + void SoundSystemAL::startEngine() { if (_initialized) return; @@ -285,7 +361,7 @@ void SoundSystemAL::startEngine() ALCenum err = alcGetError(_device); if (err != ALC_NO_ERROR) { - LOG_E("Unable To Open Audio Context: %s", alcGetString(_device, err)); + LOG_E("Unable To Open Audio Context: %ss", alcGetString(_device, err)); return; } @@ -300,6 +376,29 @@ void SoundSystemAL::startEngine() // Set Distance Model alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED); + + // From 0.10.0 + /*alGenSources(SS_AL_SOURCES, m_sources); + for (int i = 0; i < SS_AL_SOURCES; i++) + { + ALuint sid = m_sources[i]; + alSourcef(sid, AL_REFERENCE_DISTANCE, 5.0f); + alSourcef(sid, AL_MAX_DISTANCE, SS_AL_MAX_DISTANCE); + alSourcef(sid, AL_ROLLOFF_FACTOR, 6.0f); + }*/ + + // From 0.10.0 + /*const ALfloat position[12] = {}; + alListenerfv(AL_POSITION, position); + AL_ERROR_CHECK(); + const ALfloat orientation[6] = { 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f }; + alListenerfv(AL_ORIENTATION, orientation); + AL_ERROR_CHECK(); + const ALfloat velocity[3] = { }; + alListenerfv(AL_VELOCITY, velocity); + AL_ERROR_CHECK();*/ + + _musicStream = new SoundStreamAL(); // Mark As loaded _initialized = true; @@ -308,12 +407,14 @@ void SoundSystemAL::startEngine() void SoundSystemAL::stopEngine() { if (!_initialized) return; + + delete _musicStream; // Delete Audio Sources - delete_sources(); + _deleteSources(); // Delete Audio Buffers - delete_buffers(); + _deleteBuffers(); // Deselect Context alcMakeContextCurrent(NULL); diff --git a/platforms/sound/opensl/CustomSoundSystem.hpp b/platforms/sound/opensl/CustomSoundSystem.hpp index f9e37e3da..e37141fa8 100644 --- a/platforms/sound/opensl/CustomSoundSystem.hpp +++ b/platforms/sound/opensl/CustomSoundSystem.hpp @@ -41,22 +41,16 @@ class SoundSystemSL : public SoundSystem void destroy(); bool checkErr(SLresult res); void removeStoppedSounds(); - void setListenerPos(float x, float y, float z) override; - void setListenerAngle(float yaw, float pitch) override; - void load(const std::string& sound) override; - void play(const std::string& sound) override; - void pause(const std::string& sound) override; - void stop(const std::string& sound) override; - void playAt(const SoundDesc& sound, float x, float y, float z, float volume, float pitch) override; + void setListenerPos(const Vec3& pos) override; + void setListenerAngle(const Vec2& rot) override; + void playAt(const SoundDesc& sound, const Vec3& pos, float volume, float pitch) override; static void removePlayer(SLAndroidSimpleBufferQueueItf caller, void* context); private: SLSoundList m_playingSounds; SLEngineItf m_slEngine; SL3DLocationItf m_3dLocationItf; SLObjectItf m_slOutputMix; - float m_listenerX; - float m_listenerY; - float m_listenerZ; + Vec3 m_listenerPos; int m_soundCount; bool m_bAvailable; std::vector m_tempToRemove; diff --git a/platforms/sound/opensl/SoundSystemSL.cpp b/platforms/sound/opensl/SoundSystemSL.cpp index fcfca14da..bc3deaecc 100644 --- a/platforms/sound/opensl/SoundSystemSL.cpp +++ b/platforms/sound/opensl/SoundSystemSL.cpp @@ -17,9 +17,7 @@ pthread_mutex_t SoundSystemSL::toRemoveMutex; SoundSystemSL::SoundSystemSL() { - m_listenerX = 0.0f; - m_listenerY = 0.0f; - m_listenerZ = 0.0f; + m_listenerPos = Vec3::ZERO; m_soundCount = 0; m_3dLocationItf = nullptr; m_bAvailable = true; @@ -113,40 +111,38 @@ void SoundSystemSL::removeStoppedSounds() } -void SoundSystemSL::setListenerPos(float x, float y, float z) +void SoundSystemSL::setListenerPos(const Vec3& pos) { if (!m_3dLocationItf) { - m_listenerX = x; - m_listenerY = y; - m_listenerZ = z; + m_listenerPos = pos; return; } SLVec3D vec; - vec.x = int(1000.0f * x); - vec.y = int(1000.0f * y); - vec.z = int(1000.0f * z); + vec.x = int(1000.0f * pos.x); + vec.y = int(1000.0f * pos.y); + vec.z = int(1000.0f * pos.z); checkErr((*m_3dLocationItf)->SetLocationCartesian( m_3dLocationItf, &vec )); } -void SoundSystemSL::setListenerAngle(float yaw, float pitch) +void SoundSystemSL::setListenerAngle(const Vec2& rot) { if (!m_3dLocationItf) return; checkErr((*m_3dLocationItf)->SetOrientationAngles( m_3dLocationItf, - int(180000.0f * yaw), + int(180000.0f * rot.x), 0, 0 )); } -void SoundSystemSL::playAt(const SoundDesc &sound, float x, float y, float z, float volume, float pitch) +void SoundSystemSL::playAt(const SoundDesc &sound, const Vec3& pos, float volume, float pitch) { removeStoppedSounds(); @@ -206,7 +202,7 @@ void SoundSystemSL::playAt(const SoundDesc &sound, float x, float y, float z, fl checkErr((*pVolumeItf)->SetVolumeLevel(pVolumeItf, remappedVolume)); checkErr((*pAudioPlayer)->GetInterface(pAudioPlayer, SL_IID_BUFFERQUEUE, &pBufferQueueItf)); checkErr((*pBufferQueueItf)->RegisterCallback(pBufferQueueItf, removePlayer, (void*) pAudioPlayer)); - checkErr((*pBufferQueueItf)->Enqueue(pBufferQueueItf, sound.m_pData, sound.field_4)); + checkErr((*pBufferQueueItf)->Enqueue(pBufferQueueItf, sound.m_buffer.m_pData, sound.m_buffer.m_dataSize)); checkErr((*pPlayItf)->SetPlayState(pPlayItf, SL_PLAYSTATE_PLAYING)); m_playingSounds.push_back(pAudioPlayer); @@ -218,24 +214,3 @@ void SoundSystemSL::destroy() { } - -void SoundSystemSL::load(const std::string &sound) -{ - -} - -void SoundSystemSL::play(const std::string &sound) -{ - -} - -void SoundSystemSL::pause(const std::string &sound) -{ - -} - -void SoundSystemSL::stop(const std::string &sound) -{ - -} - diff --git a/platforms/windows/AppPlatform_win32.cpp b/platforms/windows/AppPlatform_win32.cpp index bde51080a..1ce44ea7e 100644 --- a/platforms/windows/AppPlatform_win32.cpp +++ b/platforms/windows/AppPlatform_win32.cpp @@ -207,16 +207,20 @@ bool AppPlatform_win32::hasFileSystemAccess() return true; } -std::string AppPlatform_win32::getPatchData() +AssetFile AppPlatform_win32::readAssetFile(const std::string& str, bool quiet) const { - std::ifstream ifs("assets/patches/patch_data.txt"); + std::string path = getAssetPath(str); + std::ifstream ifs(path, std::ios::binary | std::ios::ate); if (!ifs.is_open()) - return ""; + return AssetFile(); - std::stringstream ss; - ss << ifs.rdbuf(); + std::streamsize size = ifs.tellg(); + ifs.seekg(0, std::ios::beg); - return ss.str(); + unsigned char* buffer = new unsigned char[size]; + ifs.read((char*) buffer, size); + + return AssetFile(size, buffer); } void AppPlatform_win32::setScreenSize(int width, int height) diff --git a/platforms/windows/AppPlatform_win32.hpp b/platforms/windows/AppPlatform_win32.hpp index 76323b8b9..bc681df00 100644 --- a/platforms/windows/AppPlatform_win32.hpp +++ b/platforms/windows/AppPlatform_win32.hpp @@ -55,7 +55,7 @@ class AppPlatform_win32 : public AppPlatform bool hasFileSystemAccess() override; // Also add this to allow dynamic texture patching. - std::string getPatchData() override; + AssetFile readAssetFile(const std::string&, bool) const override; void setScreenSize(int width, int height); const char* const getWindowTitle() const { return m_WindowTitle; } diff --git a/platforms/windows/minecraftcpp.sln b/platforms/windows/minecraftcpp.sln index f66764d3e..2fa8f435f 100644 --- a/platforms/windows/minecraftcpp.sln +++ b/platforms/windows/minecraftcpp.sln @@ -1,6 +1,6 @@  Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio Version 10 +# Visual Studio Version 17 VisualStudioVersion = 17.6.33723.286 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZLib", "projects\ZLib\ZLib.vcxproj", "{5DA292FD-FA40-45D8-900A-6652C9662913}" @@ -27,6 +27,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Renderer", "projects\Render EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OpenGL", "projects\OpenGL\OpenGL.vcxproj", "{2E1A5B55-A6D4-4743-A63A-4F27DB03C0A1}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "STB", "projects\STB\STB.vcxproj", "{F332CB57-FF16-4D43-BBE0-76F28301DAA8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug_SDL2|x64 = Debug_SDL2|x64 @@ -203,6 +205,22 @@ Global {2E1A5B55-A6D4-4743-A63A-4F27DB03C0A1}.Release|x64.Build.0 = Release|x64 {2E1A5B55-A6D4-4743-A63A-4F27DB03C0A1}.Release|x86.ActiveCfg = Release|Win32 {2E1A5B55-A6D4-4743-A63A-4F27DB03C0A1}.Release|x86.Build.0 = Release|Win32 + {F332CB57-FF16-4D43-BBE0-76F28301DAA8}.Debug_SDL2|x64.ActiveCfg = Debug|x64 + {F332CB57-FF16-4D43-BBE0-76F28301DAA8}.Debug_SDL2|x64.Build.0 = Debug|x64 + {F332CB57-FF16-4D43-BBE0-76F28301DAA8}.Debug_SDL2|x86.ActiveCfg = Debug|Win32 + {F332CB57-FF16-4D43-BBE0-76F28301DAA8}.Debug_SDL2|x86.Build.0 = Debug|Win32 + {F332CB57-FF16-4D43-BBE0-76F28301DAA8}.Debug|x64.ActiveCfg = Debug|x64 + {F332CB57-FF16-4D43-BBE0-76F28301DAA8}.Debug|x64.Build.0 = Debug|x64 + {F332CB57-FF16-4D43-BBE0-76F28301DAA8}.Debug|x86.ActiveCfg = Debug|Win32 + {F332CB57-FF16-4D43-BBE0-76F28301DAA8}.Debug|x86.Build.0 = Debug|Win32 + {F332CB57-FF16-4D43-BBE0-76F28301DAA8}.Release_SDL2|x64.ActiveCfg = Release|x64 + {F332CB57-FF16-4D43-BBE0-76F28301DAA8}.Release_SDL2|x64.Build.0 = Release|x64 + {F332CB57-FF16-4D43-BBE0-76F28301DAA8}.Release_SDL2|x86.ActiveCfg = Release|Win32 + {F332CB57-FF16-4D43-BBE0-76F28301DAA8}.Release_SDL2|x86.Build.0 = Release|Win32 + {F332CB57-FF16-4D43-BBE0-76F28301DAA8}.Release|x64.ActiveCfg = Release|x64 + {F332CB57-FF16-4D43-BBE0-76F28301DAA8}.Release|x64.Build.0 = Release|x64 + {F332CB57-FF16-4D43-BBE0-76F28301DAA8}.Release|x86.ActiveCfg = Release|Win32 + {F332CB57-FF16-4D43-BBE0-76F28301DAA8}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -212,6 +230,7 @@ Global {4B7FCC5F-7E38-4934-B272-2F5BBEF51013} = {3274DB20-9315-418F-8634-A4203E0436C2} {A88F87B0-D37B-4385-A870-F349D8001E08} = {3274DB20-9315-418F-8634-A4203E0436C2} {2E1A5B55-A6D4-4743-A63A-4F27DB03C0A1} = {3274DB20-9315-418F-8634-A4203E0436C2} + {F332CB57-FF16-4D43-BBE0-76F28301DAA8} = {3274DB20-9315-418F-8634-A4203E0436C2} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {469E02FA-FBC4-419C-8EA0-3055EE0AD3AC} diff --git a/platforms/windows/projects/Client/Client.vcxproj b/platforms/windows/projects/Client/Client.vcxproj index cdfbbd235..aa82f51dd 100644 --- a/platforms/windows/projects/Client/Client.vcxproj +++ b/platforms/windows/projects/Client/Client.vcxproj @@ -129,11 +129,13 @@ + + @@ -176,6 +178,8 @@ + + @@ -257,10 +261,12 @@ + + @@ -308,6 +314,9 @@ {e43f7c6a-a099-48c9-9d37-b56cd8d6d785} + + {f332cb57-ff16-4d43-bbe0-76f28301daa8} + diff --git a/platforms/windows/projects/Client/Client.vcxproj.filters b/platforms/windows/projects/Client/Client.vcxproj.filters index e234a075c..d83f97e7f 100644 --- a/platforms/windows/projects/Client/Client.vcxproj.filters +++ b/platforms/windows/projects/Client/Client.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -284,6 +284,9 @@ Header Files\Renderer + + Header Files\Sound + Header Files\Sound @@ -296,6 +299,9 @@ Header Files\Sound + + Header Files\Sound + Header Files\Sound @@ -455,6 +461,12 @@ Header Files\Player\Input + + Header Files\Sound + + + Header Files\Sound + @@ -664,6 +676,9 @@ Source Files\Renderer + + Source Files\Sound + Source Files\Sound @@ -673,6 +688,9 @@ Source Files\Sound + + Source Files\Sound + Source Files\Sound diff --git a/platforms/windows/projects/MinecraftClient.SDL2/MinecraftClient.SDL2.vcxproj b/platforms/windows/projects/MinecraftClient.SDL2/MinecraftClient.SDL2.vcxproj index 46f98edb0..1be42c37d 100644 --- a/platforms/windows/projects/MinecraftClient.SDL2/MinecraftClient.SDL2.vcxproj +++ b/platforms/windows/projects/MinecraftClient.SDL2/MinecraftClient.SDL2.vcxproj @@ -37,7 +37,7 @@ - $(MC_ROOT)\platforms\openal;$(MC_ROOT)\thirdparty\stb_image\include;$(SDL2_PATH)\include;$(LIBPNG_PATH);$(OPENAL_PATH)\include;%(AdditionalIncludeDirectories) + $(MC_ROOT)\platforms\sound\openal;$(MC_ROOT)\thirdparty\stb_image\include;$(SDL2_PATH)\include;$(LIBPNG_PATH);$(OPENAL_PATH)\include;%(AdditionalIncludeDirectories) Console @@ -61,18 +61,20 @@ - + + - + + diff --git a/platforms/windows/projects/MinecraftClient.SDL2/MinecraftClient.SDL2.vcxproj.filters b/platforms/windows/projects/MinecraftClient.SDL2/MinecraftClient.SDL2.vcxproj.filters index 0c2cb9f2a..f60c21af6 100644 --- a/platforms/windows/projects/MinecraftClient.SDL2/MinecraftClient.SDL2.vcxproj.filters +++ b/platforms/windows/projects/MinecraftClient.SDL2/MinecraftClient.SDL2.vcxproj.filters @@ -24,7 +24,7 @@ Source Files - + Source Files @@ -33,6 +33,9 @@ Source Files + + Source Files + @@ -41,7 +44,7 @@ Header Files\Compatibility - + Header Files @@ -50,5 +53,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/platforms/windows/projects/STB/STB.vcxproj b/platforms/windows/projects/STB/STB.vcxproj new file mode 100644 index 000000000..a02a47c8e --- /dev/null +++ b/platforms/windows/projects/STB/STB.vcxproj @@ -0,0 +1,67 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {f332cb57-ff16-4d43-bbe0-76f28301daa8} + STB + STB + StaticLibrary + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/platforms/windows/projects/STB/STB.vcxproj.filters b/platforms/windows/projects/STB/STB.vcxproj.filters new file mode 100644 index 000000000..1718ea326 --- /dev/null +++ b/platforms/windows/projects/STB/STB.vcxproj.filters @@ -0,0 +1,78 @@ + + + + + {e505ae8a-bc6d-4cc0-a25d-232a1d2f5fcf} + + + {979d09b3-8d66-4441-ad0b-c9a1759c617f} + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Header Files + + + \ No newline at end of file diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index e08f8101e..f4a6ab7c0 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -63,6 +63,8 @@ add_library(reminecraftpe-core STATIC client/renderer/FoliageColor.cpp client/renderer/GrassColor.cpp client/sound/SoundData.cpp + client/sound/SoundPathRepository.cpp + client/sound/SoundStream.cpp client/sound/SoundSystem.cpp client/sound/SoundRepository.cpp client/sound/SoundEngine.cpp @@ -186,16 +188,16 @@ add_library(reminecraftpe-core STATIC world/entity/Monster.cpp world/entity/Rocket.cpp world/entity/MobFactory.cpp - world/entity/Chicken.cpp - world/entity/Cow.cpp - world/entity/Creeper.cpp - world/entity/Pig.cpp - world/entity/Sheep.cpp - world/entity/SynchedEntityData.cpp - world/entity/EntityCategories.cpp - world/entity/EntityType.cpp - world/entity/EntityTypeDescriptor.cpp - world/entity/MobCategory.cpp + world/entity/Chicken.cpp + world/entity/Cow.cpp + world/entity/Creeper.cpp + world/entity/Pig.cpp + world/entity/Sheep.cpp + world/entity/SynchedEntityData.cpp + world/entity/EntityCategories.cpp + world/entity/EntityType.cpp + world/entity/EntityTypeDescriptor.cpp + world/entity/MobCategory.cpp world/level/Dimension.cpp world/level/Material.cpp world/level/LevelListener.cpp @@ -322,15 +324,6 @@ else() target_link_libraries(zlib-interface INTERFACE ZLIB::ZLIB) endif() target_link_libraries(reminecraftpe-core PUBLIC zlib-interface) - -# Sound Data -if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../sound_data/sounds.h") - if(NOT DEFINED ENV{CI}) - message(WARNING "Missing sound data! Did you run tools/grabsounds.py?") - endif() - target_compile_definitions(reminecraftpe-core PRIVATE MISSING_SOUND_DATA) -endif() - # OpenGL Support Code if(WIN32) target_sources(reminecraftpe-core PRIVATE ../thirdparty/GL/GLExt.cpp) diff --git a/source/client/app/AppPlatform.cpp b/source/client/app/AppPlatform.cpp index b90592c86..08fa245a1 100644 --- a/source/client/app/AppPlatform.cpp +++ b/source/client/app/AppPlatform.cpp @@ -226,7 +226,15 @@ bool AppPlatform::hasFileSystemAccess() std::string AppPlatform::getPatchData() { - return ""; + const AssetFile file = readAssetFile("patches/patch_data.txt", false); + std::string out = std::string(file.data, file.data + file.size); + delete file.data; + return out; +} + +AssetFile AppPlatform::readAssetFile(const std::string& path, bool quiet) const +{ + return AssetFile(); } void AppPlatform::initSoundSystem() diff --git a/source/client/app/AppPlatform.hpp b/source/client/app/AppPlatform.hpp index 51ddc7945..0a03bfea2 100644 --- a/source/client/app/AppPlatform.hpp +++ b/source/client/app/AppPlatform.hpp @@ -13,6 +13,7 @@ #include "client/renderer/Texture.hpp" #include "client/sound/SoundSystem.hpp" +#include "AssetFile.hpp" class AppPlatform { @@ -86,6 +87,8 @@ class AppPlatform virtual std::string getPatchData(); virtual void initSoundSystem(); virtual SoundSystem* const getSoundSystem() const; + // Used For Sounds + virtual AssetFile readAssetFile(const std::string&, bool) const; #endif public: diff --git a/source/client/app/AssetFile.hpp b/source/client/app/AssetFile.hpp new file mode 100644 index 000000000..b0f2c0316 --- /dev/null +++ b/source/client/app/AssetFile.hpp @@ -0,0 +1,19 @@ +/******************************************************************** +Minecraft: Pocket Edition - Decompilation Project + Copyright (C) 2023 iProgramInCpp + + The following code is licensed under the BSD 1 clause license. + SPDX-License-Identifier: BSD-1-Clause + ********************************************************************/ + +#pragma once + +#include + +struct AssetFile { + int64_t size; + unsigned char *data; + + AssetFile(): size(-1), data(nullptr) {} + AssetFile(int64_t size, unsigned char *data): size(size), data(data) {} +}; \ No newline at end of file diff --git a/source/client/app/Minecraft.cpp b/source/client/app/Minecraft.cpp index e0f5817b9..fbd16a5ef 100644 --- a/source/client/app/Minecraft.cpp +++ b/source/client/app/Minecraft.cpp @@ -734,13 +734,8 @@ void Minecraft::tick() m_pParticleEngine->tick(); #ifndef ORIGINAL_CODE - if (m_pMobPersp) - { - m_pSoundEngine->m_pSoundSystem->setListenerPos(m_pMobPersp->m_pos.x, m_pMobPersp->m_pos.y, m_pMobPersp->m_pos.z); - m_pSoundEngine->m_pSoundSystem->setListenerAngle(m_pMobPersp->m_rot.x, m_pMobPersp->m_rot.y); - } + m_pSoundEngine->update(m_pMobPersp, m_timer.m_renderTicks); #endif - } if (m_pScreen) @@ -837,8 +832,8 @@ void Minecraft::init() GetPatchManager()->PatchTiles(); - m_pSoundEngine = new SoundEngine(platform()->getSoundSystem()); - m_pSoundEngine->init(m_options); + m_pSoundEngine = new SoundEngine(platform()->getSoundSystem(), 20.0f); // 20.0f on 0.7.0 + m_pSoundEngine->init(m_options, platform()); m_pLevelRenderer = new LevelRenderer(this, m_pTextures); m_pGameRenderer = new GameRenderer(this); @@ -866,6 +861,7 @@ Minecraft::~Minecraft() SAFE_DELETE(m_pLevelRenderer); SAFE_DELETE(m_pGameRenderer); SAFE_DELETE(m_pParticleEngine); + m_pSoundEngine->destroy(); SAFE_DELETE(m_pSoundEngine); SAFE_DELETE(m_pGameMode); SAFE_DELETE(m_pFont); diff --git a/source/client/gui/Gui.cpp b/source/client/gui/Gui.cpp index 148a270fa..8f9a7a9fb 100644 --- a/source/client/gui/Gui.cpp +++ b/source/client/gui/Gui.cpp @@ -288,9 +288,9 @@ void Gui::render(float f, bool bHaveScreen, int mouseX, int mouseY) int emptyHeartX = 16; bool b1 = false; - if (player->field_B8 < 10) + if (player->m_invulnerableTime < 10) { - b1 = player->field_B8 / 3 % 2; + b1 = player->m_invulnerableTime / 3 % 2; emptyHeartX += 9 * b1; } @@ -318,9 +318,9 @@ void Gui::render(float f, bool bHaveScreen, int mouseX, int mouseY) if (b1) { - if (healthNo < player->field_100) + if (healthNo < player->m_lastHealth) blit(heartX, heartY, 70, 0, 9, 9, 0, 0); - else if (healthNo == player->field_100) + else if (healthNo == player->m_lastHealth) blit(heartX, heartY, 79, 0, 9, 9, 0, 0); } diff --git a/source/client/gui/Screen.cpp b/source/client/gui/Screen.cpp index 99e7cb4b0..aeb233a2e 100644 --- a/source/client/gui/Screen.cpp +++ b/source/client/gui/Screen.cpp @@ -63,7 +63,7 @@ void Screen::keyPressed(int key) { if (m_buttonTabList[m_tabButtonIndex]->m_bEnabled) { - m_pMinecraft->m_pSoundEngine->play("random.click"); + m_pMinecraft->m_pSoundEngine->playUI("random.click"); buttonClicked(m_buttonTabList[m_tabButtonIndex]); } } @@ -210,7 +210,7 @@ void Screen::mouseClicked(int xPos, int yPos, int d) // d = clicked? if (!m_pMinecraft->isTouchscreen()) { - m_pMinecraft->m_pSoundEngine->play("random.click"); + m_pMinecraft->m_pSoundEngine->playUI("random.click"); buttonClicked(button); } } @@ -264,7 +264,7 @@ void Screen::mouseReleased(int xPos, int yPos, int d) { if (m_pMinecraft->isTouchscreen() && m_pClickedButton->clicked(m_pMinecraft, xPos, yPos)) { - m_pMinecraft->m_pSoundEngine->play("random.click"); + m_pMinecraft->m_pSoundEngine->playUI("random.click"); buttonClicked(m_pClickedButton); } m_pClickedButton->released(xPos, yPos); diff --git a/source/client/gui/screens/IngameBlockSelectionScreen.cpp b/source/client/gui/screens/IngameBlockSelectionScreen.cpp index 6fc7d4449..9af461270 100644 --- a/source/client/gui/screens/IngameBlockSelectionScreen.cpp +++ b/source/client/gui/screens/IngameBlockSelectionScreen.cpp @@ -201,6 +201,6 @@ void IngameBlockSelectionScreen::selectSlotAndClose() pInv->selectItem(m_selectedSlot, m_pMinecraft->m_gui.getNumUsableSlots()); - m_pMinecraft->m_pSoundEngine->play("random.click"); + m_pMinecraft->m_pSoundEngine->playUI("random.click"); m_pMinecraft->setScreen(nullptr); } diff --git a/source/client/options/Options.cpp b/source/client/options/Options.cpp index 57a43ca1e..e863d74ef 100644 --- a/source/client/options/Options.cpp +++ b/source/client/options/Options.cpp @@ -38,7 +38,7 @@ void Options::_initDefaultValues() m_bDontRenderGui = false; field_248 = 1.0f; m_bThirdPerson = false; - field_0 = 1.0f; + m_fMusicVolume = 1.0f; field_23E = 0; m_fMasterVolume = 1.0f; m_bFlyCheat = false; diff --git a/source/client/options/Options.hpp b/source/client/options/Options.hpp index 6bb24c7db..7dfe9fbcf 100644 --- a/source/client/options/Options.hpp +++ b/source/client/options/Options.hpp @@ -95,7 +95,7 @@ class Options KeyMapping m_keyMappings[KM_COUNT]; public: - float field_0; + float m_fMusicVolume; float m_fMasterVolume; float m_fSensitivity; bool m_bInvertMouse; diff --git a/source/client/renderer/GameRenderer.cpp b/source/client/renderer/GameRenderer.cpp index ff7e3170a..e6b1afddd 100644 --- a/source/client/renderer/GameRenderer.cpp +++ b/source/client/renderer/GameRenderer.cpp @@ -200,8 +200,8 @@ void GameRenderer::moveCameraToPlayer(float f) if (!m_pMinecraft->getOptions()->field_241) { - glRotatef(pMob->m_rotPrev.y + f * (pMob->m_rot.y - pMob->m_rotPrev.y), 1.0f, 0.0f, 0.0f); - glRotatef(pMob->m_rotPrev.x + f * (pMob->m_rot.x - pMob->m_rotPrev.x) + 180.0f, 0.0f, 1.0f, 0.0f); + glRotatef(pMob->m_oRot.y + f * (pMob->m_rot.y - pMob->m_oRot.y), 1.0f, 0.0f, 0.0f); + glRotatef(pMob->m_oRot.x + f * (pMob->m_rot.x - pMob->m_oRot.x) + 180.0f, 0.0f, 1.0f, 0.0f); } glTranslatef(0.0f, headHeightDiff, 0.0f); diff --git a/source/client/renderer/ItemInHandRenderer.cpp b/source/client/renderer/ItemInHandRenderer.cpp index 0c9c0b0b5..98c77ba81 100644 --- a/source/client/renderer/ItemInHandRenderer.cpp +++ b/source/client/renderer/ItemInHandRenderer.cpp @@ -170,8 +170,8 @@ void ItemInHandRenderer::render(float f) float h = m_oHeight + (m_height - m_oHeight) * f; glPushMatrix(); - glRotatef(pLP->m_rotPrev.y + (pLP->m_rot.y - pLP->m_rotPrev.y) * f, 1.0f, 0.0f, 0.0f); - glRotatef(pLP->m_rotPrev.x + (pLP->m_rot.x - pLP->m_rotPrev.x) * f, 0.0f, 1.0f, 0.0f); + glRotatef(pLP->m_oRot.y + (pLP->m_rot.y - pLP->m_oRot.y) * f, 1.0f, 0.0f, 0.0f); + glRotatef(pLP->m_oRot.x + (pLP->m_rot.x - pLP->m_oRot.x) * f, 0.0f, 1.0f, 0.0f); Lighting::turnOn(); // must be called before glPopMatrix() glPopMatrix(); diff --git a/source/client/renderer/entity/EntityRenderDispatcher.cpp b/source/client/renderer/entity/EntityRenderDispatcher.cpp index ae3ef15c8..d7d18a29d 100644 --- a/source/client/renderer/entity/EntityRenderDispatcher.cpp +++ b/source/client/renderer/entity/EntityRenderDispatcher.cpp @@ -127,14 +127,14 @@ void EntityRenderDispatcher::prepare(Level* level, Textures* textures, Font* fon m_pMob = mob; m_pFont = font; m_pOptions = options; - m_rot = mob->m_rotPrev + (mob->m_rot - mob->m_rotPrev) * f; + m_rot = mob->m_oRot + (mob->m_rot - mob->m_oRot) * f; m_pos = mob->m_posPrev + (mob->m_pos - mob->m_posPrev) * f; } void EntityRenderDispatcher::render(Entity* entity, float a) { Vec3 pos = Vec3(entity->m_posPrev + (entity->m_pos - entity->m_posPrev) * a); - float yaw = entity->m_rotPrev.x + a * (entity->m_rot.x - entity->m_rotPrev.x); + float yaw = entity->m_oRot.x + a * (entity->m_rot.x - entity->m_oRot.x); float bright = entity->getBrightness(1.0f); glColor4f(bright, bright, bright, 1.0f); diff --git a/source/client/renderer/entity/MobRenderer.cpp b/source/client/renderer/entity/MobRenderer.cpp index d5b3fde6c..36cf254b5 100644 --- a/source/client/renderer/entity/MobRenderer.cpp +++ b/source/client/renderer/entity/MobRenderer.cpp @@ -95,8 +95,8 @@ void MobRenderer::render(Entity* entity, float x, float y, float z, float unused m_pArmorModel->m_bIsBaby = m_pModel->m_bIsBaby; } - float aYaw = pMob->m_rotPrev.x + (pMob->m_rot.x - pMob->m_rotPrev.x) * f; - float aPitch = pMob->m_rotPrev.y + (pMob->m_rot.y - pMob->m_rotPrev.y) * f; + float aYaw = pMob->m_oRot.x + (pMob->m_rot.x - pMob->m_oRot.x) * f; + float aPitch = pMob->m_oRot.y + (pMob->m_rot.y - pMob->m_oRot.y) * f; float fBob = getBob(pMob, f); float fSmth = pMob->field_EC + (pMob->field_E8 - pMob->field_EC) * f; @@ -109,10 +109,10 @@ void MobRenderer::render(Entity* entity, float x, float y, float z, float unused //glTranslatef(0.0f, -1.5078f, 0.0f); glTranslatef(0.0f, -24.0f * fScale - (1.0f / 128.0f), 0.0f); - float x1 = pMob->field_128 + (pMob->field_12C - pMob->field_128) * f; + float x1 = pMob->field_128 + (pMob->m_walkAnimSpeed - pMob->field_128) * f; if (x1 > 1.0f) x1 = 1.0f; - float x2 = pMob->field_130 - pMob->field_12C * (1.0f - f); + float x2 = pMob->field_130 - pMob->m_walkAnimSpeed * (1.0f - f); bindTexture(pMob->getTexture()); glEnable(GL_ALPHA_TEST); diff --git a/source/client/sound/SoundData.cpp b/source/client/sound/SoundData.cpp index c1cabd486..823642efd 100644 --- a/source/client/sound/SoundData.cpp +++ b/source/client/sound/SoundData.cpp @@ -6,15 +6,122 @@ SPDX-License-Identifier: BSD-1-Clause ********************************************************************/ +#define STB_VORBIS_HEADER_ONLY +#include "thirdparty/stb_image/include/stb_vorbis.c" + #include "SoundData.hpp" -// -------------------------------------------------------------------- -// WARNING! If you have an error here it is most likely because you did -// not grab the sound data from a working Minecraft PE 0.1.3 .apk. -// -// Check the readme for a guide on how to extract game sounds from the -// Minecraft PE 0.1.3 .apk file. -// -------------------------------------------------------------------- -#ifndef MISSING_SOUND_DATA -#include "../../sound_data/sounds.h" -#endif +#include "client/app/AppPlatform.hpp" + +std::string SoundDesc::dirs[] = { + "sound", + "newsound", + "sound3" +}; + +bool SoundDesc::_load(const AppPlatform* platform, const char* category, const char *name) +{ + if (m_isLoaded) + { + // Already Loaded + LOG_W("Sound \"%s\" is already loaded!", name); + return true; + } + + // Load + if (_loadOgg(platform, category, name) || _loadPcm(platform, name)) + { + // Success! + return true; + } + else + { + m_codecType = AudioCodec::NONE; + LOG_W("Failed to load sound \"%s\"!", name); + return false; + } +} + +bool SoundDesc::_loadPcm(const AppPlatform* platform, const char *name) +{ + m_file = platform->readAssetFile(std::string("sound/") + name + ".pcm", true); + m_isLoaded = m_file.size > 0; + + // Error + if (!m_isLoaded) return false; + + m_codecType = AudioCodec::PCM; + m_fileData = m_file.data; + m_header = *(PCMSoundHeader *) m_fileData; + m_buffer.m_pData = (void *) (m_fileData + sizeof(PCMSoundHeader)); + m_buffer.m_dataSize = m_header.m_channels * m_header.m_length * m_header.m_bytes_per_sample; + + // Success! + return true; +} + +bool SoundDesc::_loadOgg(const AppPlatform* platform, const char* category, const char *name) +{ + for (int i = 0; i < SOUND_DIRS_SIZE; i++) + { + m_file = platform->readAssetFile(dirs[i] + '/' + category + '/' + name + ".ogg", true); + m_isLoaded = m_file.size > 0; + if (m_isLoaded) break; + } + + // Error + if (!m_isLoaded) return false; + + m_codecType = AudioCodec::OGG; + m_fileData = m_file.data; + m_header.m_bytes_per_sample = 2; // Always 2 (16-bit) + // Casting to a short** here might cause problems. Let's find out... + // Seems like it doesn't. Cool. + m_header.m_length = stb_vorbis_decode_memory(m_file.data, (int) m_file.size, &m_header.m_channels, &m_header.m_sample_rate, (short **) &m_buffer.m_pData); + if (m_header.m_length == -1) + { + LOG_E("An error occurred while trying to decode a sound!"); + return false; + } + m_buffer.m_dataSize = m_header.m_channels * m_header.m_length * m_header.m_bytes_per_sample; + + // Success! + return true; +} + +void SoundDesc::_unload() +{ + if (!m_isLoaded) + { + // Sound is already unloaded + return; + } + // Free OGG Data + if (m_codecType == AudioCodec::OGG) { + free(m_buffer.m_pData); + } + // Free File Data + delete m_file.data; + m_isLoaded = false; +} + +// Load All Sounds +void SoundDesc::_loadAll(const AppPlatform* platform) +{ +#define SOUND(category, name, number) SA_##name##number._load(platform, #category, #name#number); +#include "sound_list.h" +#undef SOUND +} + +// Un-load All Sounds +void SoundDesc::_unloadAll() +{ +#define SOUND(category, name, number) SA_##name##number._unload(); +#include "sound_list.h" +#undef SOUND +} + +// Declare Variables +#define SOUND(category, name, number) SoundDesc SA_##name##number; +#include "sound_list.h" +#undef SOUND \ No newline at end of file diff --git a/source/client/sound/SoundData.hpp b/source/client/sound/SoundData.hpp index 6769bc73b..34b78e47d 100644 --- a/source/client/sound/SoundData.hpp +++ b/source/client/sound/SoundData.hpp @@ -10,6 +10,20 @@ #include #include "common/Utils.hpp" +#include "client/app/AssetFile.hpp" + +class AppPlatform; + +class AudioCodec +{ +public: + enum Type + { + NONE, + PCM, + OGG + }; +}; struct PCMSoundHeader { @@ -19,24 +33,37 @@ struct PCMSoundHeader int m_length; }; -struct SoundDesc +struct SoundBuffer { - uint16_t* m_pData; - int field_4; - PCMSoundHeader m_header; - PCMSoundHeader* m_pHeader; + void* m_pData; + int m_dataSize; +}; - SoundDesc() - { - m_pData = nullptr; - field_4 = 0; - m_pHeader = nullptr; - } - SoundDesc(PCMSoundHeader& header, uint16_t* data) - { - m_pHeader = &header; - m_header = header; - m_pData = data; - field_4 = header.m_channels * header.m_length * header.m_bytes_per_sample; - } +struct AudioDescriptor +{ + bool m_isLoaded; + AudioCodec::Type m_codecType; + PCMSoundHeader m_header; +}; + +#define SOUND_DIRS_SIZE 3 + +struct SoundDesc : AudioDescriptor +{ + static std::string dirs[SOUND_DIRS_SIZE]; + + AssetFile m_file; + SoundBuffer m_buffer; + unsigned char* m_fileData; + + bool _load(const AppPlatform* platform, const char* category, const char *name); + bool _loadPcm(const AppPlatform* platform, const char *name); + bool _loadOgg(const AppPlatform* platform, const char* category, const char *name); + void _unload(); + static void _loadAll(const AppPlatform*); + static void _unloadAll(); }; + +#define SOUND(category, name, number) extern SoundDesc SA_##name##number; +#include "sound_list.h" +#undef SOUND diff --git a/source/client/sound/SoundDefs.hpp b/source/client/sound/SoundDefs.hpp index e39aaec3c..9a759a06c 100644 --- a/source/client/sound/SoundDefs.hpp +++ b/source/client/sound/SoundDefs.hpp @@ -8,37 +8,3 @@ #pragma once #include "SoundData.hpp" - -// These are defined in SoundData.cpp, if MISSING_SOUND_DATA isn't declared. - -#ifndef MISSING_SOUND_DATA - -extern SoundDesc SA_cloth1; -extern SoundDesc SA_cloth2; -extern SoundDesc SA_cloth3; -extern SoundDesc SA_cloth4; -extern SoundDesc SA_grass1; -extern SoundDesc SA_grass2; -extern SoundDesc SA_grass3; -extern SoundDesc SA_grass4; -extern SoundDesc SA_gravel1; -extern SoundDesc SA_gravel2; -extern SoundDesc SA_gravel3; -extern SoundDesc SA_gravel4; -extern SoundDesc SA_sand1; -extern SoundDesc SA_sand2; -extern SoundDesc SA_sand3; -extern SoundDesc SA_sand4; -extern SoundDesc SA_stone1; -extern SoundDesc SA_stone2; -extern SoundDesc SA_stone3; -extern SoundDesc SA_stone4; -extern SoundDesc SA_wood1; -extern SoundDesc SA_wood2; -extern SoundDesc SA_wood3; -extern SoundDesc SA_wood4; -extern SoundDesc SA_click; -extern SoundDesc SA_explode; -extern SoundDesc SA_splash; - -#endif diff --git a/source/client/sound/SoundEngine.cpp b/source/client/sound/SoundEngine.cpp index d67a9ec32..335ba066c 100644 --- a/source/client/sound/SoundEngine.cpp +++ b/source/client/sound/SoundEngine.cpp @@ -1,70 +1,52 @@ /******************************************************************** - Minecraft: Pocket Edition - Decompilation Project - Copyright (C) 2023 iProgramInCpp - - The following code is licensed under the BSD 1 clause license. - SPDX-License-Identifier: BSD-1-Clause + Minecraft: Pocket Edition - Decompilation Project + Copyright (C) 2023 iProgramInCpp + + The following code is licensed under the BSD 1 clause license. + SPDX-License-Identifier: BSD-1-Clause ********************************************************************/ #include "SoundEngine.hpp" #include "SoundDefs.hpp" #include "common/Mth.hpp" +#include "world/entity/Mob.hpp" +#include "client/app/AppPlatform.hpp" -SoundEngine::SoundEngine(SoundSystem* soundSystem) +SoundEngine::SoundEngine(SoundSystem* soundSystem, float distance) { - m_pSoundSystem = soundSystem; - m_pOptions = nullptr; - field_40 = 0; - field_A1C = 0; - field_A20 = 0; - m_muted = false; + m_pSoundSystem = soundSystem; + m_pOptions = nullptr; + field_40 = 0; + m_listenerPosition = Vec3::ZERO; + m_listenerOrientation = Vec2::ZERO; + m_soundDistance = 1.0f / distance; + m_noMusicDelay = m_random.nextInt(12000); + field_A20 = 0; + m_muted = false; } float SoundEngine::_getVolumeMult(const Vec3& pos) { - return 1.0f; + // Taken from 0.7.0. Very similar to paulscode.sound.libraries.SourceLWJGLOpenAL.calculateGain() + float distance = 1.1f - (pos.distanceTo(m_listenerPosition) * m_soundDistance); + return Mth::clamp(distance, -1.0f, 1.0f); } -void SoundEngine::init(Options* options) +void SoundEngine::init(Options* options, AppPlatform* platform) { - // TODO: Who's the genius who decided it'd be better to check a name string rather than an enum? - m_pOptions = options; - -#ifndef MISSING_SOUND_DATA - m_repository.add("step.cloth", SA_cloth1); - m_repository.add("step.cloth", SA_cloth2); - m_repository.add("step.cloth", SA_cloth3); - m_repository.add("step.cloth", SA_cloth4); - - m_repository.add("step.grass", SA_grass1); - m_repository.add("step.grass", SA_grass2); - m_repository.add("step.grass", SA_grass3); - m_repository.add("step.grass", SA_grass4); - - m_repository.add("step.gravel", SA_gravel1); - m_repository.add("step.gravel", SA_gravel2); - m_repository.add("step.gravel", SA_gravel3); - m_repository.add("step.gravel", SA_gravel4); - - m_repository.add("step.sand", SA_sand1); - m_repository.add("step.sand", SA_sand2); - m_repository.add("step.sand", SA_sand3); - m_repository.add("step.sand", SA_sand4); - - m_repository.add("step.stone", SA_stone1); - m_repository.add("step.stone", SA_stone2); - m_repository.add("step.stone", SA_stone3); - m_repository.add("step.stone", SA_stone4); - - m_repository.add("step.wood", SA_wood1); - m_repository.add("step.wood", SA_wood2); - m_repository.add("step.wood", SA_wood3); - m_repository.add("step.wood", SA_wood4); - - m_repository.add("random.splash", SA_splash); - m_repository.add("random.explode", SA_explode); - m_repository.add("random.click", SA_click); -#endif + // TODO: Who's the genius who decided it'd be better to check a name string rather than an enum? + m_pOptions = options; + // Load Sounds + SoundDesc::_loadAll(platform); + +#define SOUND(category, name, number) m_sounds.add(#category "." #name, SA_##name##number); +#include "sound_list.h" +#undef SOUND + +#define MUSIC(name, number) m_songs.add(#name, platform->getAssetPath("music/" #name #number ".ogg")); +#define NEWMUSIC(name, number) m_songs.add(#name, platform->getAssetPath("newmusic/" #name #number ".ogg")); +#include "music_list.h" +#undef MUSIC } void SoundEngine::enable(bool b) @@ -77,29 +59,122 @@ void SoundEngine::updateOptions() void SoundEngine::mute() { - m_muted = true; + m_muted = true; } void SoundEngine::unMute() { - m_muted = false; + m_muted = false; } void SoundEngine::destroy() { + // Un-load Sounds + SoundDesc::_unloadAll(); +} + +void SoundEngine::playMusicTick() +{ + if (m_pOptions->m_fMusicVolume <= 0.0f) + return; + + if (!m_pSoundSystem->isPlayingMusic()/* && !soundSystem.playing("streaming")*/) + { + if (m_noMusicDelay > 0) + { + --m_noMusicDelay; + return; + } + + std::string songPath; + if (m_songs.any(songPath)) + { + m_noMusicDelay = m_random.nextInt(12000) + 12000; + m_pSoundSystem->setMusicVolume(m_pOptions->m_fMusicVolume); + m_pSoundSystem->playMusic(songPath); + } + } +} + + +void SoundEngine::update(const Mob* player, float elapsedTime) +{ + if (m_pOptions->m_fMasterVolume > 0.0f) + { + if (player != nullptr) + { + Vec3 pos = player->getPos(elapsedTime); + pos.y -= player->m_heightOffset; + m_listenerPosition = pos; + m_pSoundSystem->setListenerPos(pos); + + Vec2 rot = player->getRot(elapsedTime); + m_listenerOrientation = rot; + m_pSoundSystem->setListenerAngle(rot); + } + } + + assert(m_pSoundSystem->isAvailable()); + + m_pSoundSystem->update(elapsedTime); } void SoundEngine::play(const std::string& name, const Vec3& pos, float volume, float pitch) { - float vol = m_pOptions->m_fMasterVolume * volume; - if (vol <= 0.0f) - return; + float vol = m_pOptions->m_fMasterVolume * volume; + if (vol <= 0.0f) + return; + Vec3 nPos; + float distance = pos.distanceTo(m_listenerPosition); + if (distance > SOUND_MAX_DISTANCE) + return; + // @HACK: Annoying hack because DirectSound is making steps in 2D insanely quiet. +#ifdef USE_OPENAL + if (distance < SOUND_ATTENUATION_MIN_DISTANCE) + nPos = Vec3::ZERO; + else + nPos = pos; +#else + nPos = pos; +#endif + + float cVolume = Mth::clamp(_getVolumeMult(pos) * vol, 0.0f, 1.0f); + float cPitch = Mth::clamp(pitch, 0.5f, 2.0f); // Clamp to values specified by Paulscode + SoundDesc sd; + + if (m_sounds.get(name, sd)) + { + m_pSoundSystem->playAt(sd, nPos, cVolume, cPitch); + } +} + +void SoundEngine::playUI(const std::string& name, float volume, float pitch) +{ + volume *= 0.25F; // present on Java b1.2_02, but not Pocket for some reason + float vol = m_pOptions->m_fMasterVolume * volume; + if (vol <= 0.0f) + return; + + float cVolume = Mth::clamp(vol, 0.0f, 1.0f); + SoundDesc sd; + + if (m_sounds.get(name, sd)) + { + m_pSoundSystem->playAt(sd, Vec3::ZERO, cVolume, pitch); + } +} + +void SoundEngine::playMusic(const std::string& name) +{ + float vol = m_pOptions->m_fMusicVolume; + if (vol <= 0.0f) + return; - float cVolume = Mth::clamp(_getVolumeMult(pos) * vol, 0.0f, 1.0f); - float cPitch = Mth::clamp(pitch, -1.0f, 1.0f); - SoundDesc sd; + std::string path; - if (m_repository.get(name, sd)) { - m_pSoundSystem->playAt(sd, pos.x, pos.y, pos.z, cVolume, pitch); - } + if (m_songs.get(name, path)) + { + m_pSoundSystem->setMusicVolume(vol); + m_pSoundSystem->playMusic(path); + } } diff --git a/source/client/sound/SoundEngine.hpp b/source/client/sound/SoundEngine.hpp index 40012fc56..0b91fb023 100644 --- a/source/client/sound/SoundEngine.hpp +++ b/source/client/sound/SoundEngine.hpp @@ -11,31 +11,46 @@ #include "client/options/Options.hpp" #include "common/Random.hpp" #include "world/phys/Vec3.hpp" +#include "world/phys/Vec2.hpp" #include "SoundSystem.hpp" #include "SoundRepository.hpp" +#include "SoundPathRepository.hpp" +#include "SoundStream.hpp" + +class Mob; class SoundEngine { private: - float _getVolumeMult(const Vec3& pos); + float _getVolumeMult(const Vec3& pos); public: - SoundEngine(SoundSystem* soundSystem); - void init(Options*); - void enable(bool b); - void updateOptions(); - void mute(); - void unMute(); - void destroy(); - void play(const std::string& name, const Vec3& pos = Vec3::ZERO, float volume = 1.0f, float pitch = 1.0f); + SoundEngine(SoundSystem* soundSystem, float distance); + void init(Options*, AppPlatform*); + void enable(bool b); + void updateOptions(); + void mute(); + void unMute(); + void destroy(); + void playMusicTick(); + void update(const Mob* player, float elapsedTime); + void play(const std::string& name, const Vec3& pos = Vec3::ZERO, float volume = 1.0f, float pitch = 1.0f); + void playUI(const std::string& name, float volume = 1.0f, float pitch = 1.0f); + void playMusic(const std::string& name); public: - SoundSystem* m_pSoundSystem; - Options* m_pOptions; - int field_40; - //Random m_random; - SoundRepository m_repository; - int field_A1C; - int field_A20; - bool m_muted; + SoundSystem* m_pSoundSystem; +private: + SoundRepository m_sounds; + SoundPathRepository m_songs; + Options* m_pOptions; + int field_40; + Random m_random; + Vec3 m_listenerPosition; + Vec2 m_listenerOrientation; + float m_soundDistance; + int m_noMusicDelay; + int field_A20; + bool m_muted; + //std::vector m_streams; }; diff --git a/source/client/sound/SoundPathRepository.cpp b/source/client/sound/SoundPathRepository.cpp new file mode 100644 index 000000000..c959c4412 --- /dev/null +++ b/source/client/sound/SoundPathRepository.cpp @@ -0,0 +1,47 @@ +#include "SoundPathRepository.hpp" + +#include "common/Utils.hpp" +#include "common/Mth.hpp" + +void SoundPathRepository::add(const std::string& name, const std::string& path) +{ + _all.push_back(path); + + std::map >::iterator iter = m_repo.find(name); + if (iter == m_repo.end()) + { + std::vector paths; + paths.push_back(path); + m_repo.insert(std::make_pair(name, paths)); + } + else + { + iter->second.push_back(path); + } +} + +bool SoundPathRepository::get(const std::string& name, std::string& path) +{ + // TODO: Who's the genius who decided it'd be better to check a name string rather than an enum? + std::map >::iterator iter = m_repo.find(name); + if (iter == m_repo.end()) + { + LOG_E("Couldn't find a sound with id: %s", name.c_str()); + return false; + } + + int index = Mth::random(int(iter->second.size())); + + path = iter->second[index]; + + return true; +} + +bool SoundPathRepository::any(std::string& path) +{ + if (_all.empty()) + return false; + + path = _all.at(Mth::random(_all.size()-1)); + return true; +} \ No newline at end of file diff --git a/source/client/sound/SoundPathRepository.hpp b/source/client/sound/SoundPathRepository.hpp new file mode 100644 index 000000000..76eb388a5 --- /dev/null +++ b/source/client/sound/SoundPathRepository.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include + +class SoundPathRepository +{ +private: + std::vector _all; + +public: + void add(const std::string& name, const std::string& path); + bool get(const std::string& name, std::string& path); + bool any(std::string& path); + +public: + std::map > m_repo; +}; + diff --git a/source/client/sound/SoundRepository.cpp b/source/client/sound/SoundRepository.cpp index b2e54c9e7..1b7d3e32a 100644 --- a/source/client/sound/SoundRepository.cpp +++ b/source/client/sound/SoundRepository.cpp @@ -12,12 +12,14 @@ void SoundRepository::add(const std::string& name, SoundDesc& sd) { + if (!sd.m_isLoaded) + return; std::map >::iterator iter = m_repo.find(name); if (iter == m_repo.end()) { std::vector sdv; sdv.push_back(sd); - m_repo.insert(std::pair >(name, sdv)); + m_repo.insert(std::make_pair(name, sdv)); } else { diff --git a/source/client/sound/SoundRepository.hpp b/source/client/sound/SoundRepository.hpp index 01a801f58..6b33b710a 100644 --- a/source/client/sound/SoundRepository.hpp +++ b/source/client/sound/SoundRepository.hpp @@ -22,4 +22,3 @@ class SoundRepository public: std::map > m_repo; }; - diff --git a/source/client/sound/SoundStream.cpp b/source/client/sound/SoundStream.cpp new file mode 100644 index 000000000..7b1456263 --- /dev/null +++ b/source/client/sound/SoundStream.cpp @@ -0,0 +1,111 @@ +#include "SoundStream.hpp" + +SoundStream::SoundStream() +{ + m_volume = 1.0f; + m_bIsStreaming = false; + m_bIsPaused = false; + m_bShouldLoop = false; + + m_decoder = nullptr; + m_info = stb_vorbis_info(); + m_totalSamplesLeft = 0; + + m_tempPcmBuffer.m_dataSize = 4096 * 8; + m_tempPcmBuffer.m_pData = new int16_t[m_tempPcmBuffer.m_dataSize]; +} + +SoundStream::~SoundStream() +{ + _deleteDecoder(); + + delete[] m_tempPcmBuffer.m_pData; +} + +void SoundStream::_deleteDecoder() +{ + if (m_decoder != nullptr) + stb_vorbis_close(m_decoder); + m_decoder = nullptr; +} + +bool SoundStream::_stream(int bufferId) +{ + int size = 0; + int result = 0; + + while (size < m_tempPcmBuffer.m_dataSize) + { + result = stb_vorbis_get_samples_short_interleaved(m_decoder, m_info.channels, (short*)m_tempPcmBuffer.m_pData + size, m_tempPcmBuffer.m_dataSize - size); + if (result > 0) size += result * m_info.channels; + else break; + } + + if (size == 0) return false; + + _publishBuffer(bufferId, m_tempPcmBuffer); + m_totalSamplesLeft -= size; + + return true; +} + +void SoundStream::setVolume(float vol) +{ + if (m_volume == vol) + return; + + _setVolume(vol); + + m_volume = vol; +} + +void SoundStream::setPausedState(bool isPaused) +{ + if (m_bIsPaused == isPaused) + return; + + if (isPaused == true) + _pause(); + else + _play(); + + m_bIsPaused = isPaused; +} + +bool SoundStream::open(const std::string& fileName) +{ + if (isStreaming()) + { + close(); + } + + m_decoder = stb_vorbis_open_filename(fileName.c_str(), NULL, NULL); + if (!m_decoder) return false; + // Get file info + m_info = stb_vorbis_get_info(m_decoder); + + if (!_open(fileName)) return false; + + setPausedState(false); + + m_totalSamplesLeft = stb_vorbis_stream_length_in_samples(m_decoder) * m_info.channels; + m_bIsStreaming = true; + + return true; +} + +void SoundStream::close() +{ + _close(); + + _deleteDecoder(); + m_totalSamplesLeft = 0; + m_bIsStreaming = false; +} + +void SoundStream::update() +{ + if (!isPlaying()) return; + + _update(); +} diff --git a/source/client/sound/SoundStream.hpp b/source/client/sound/SoundStream.hpp new file mode 100644 index 000000000..ec0a8f68c --- /dev/null +++ b/source/client/sound/SoundStream.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include + +#define STB_VORBIS_HEADER_ONLY +#include "thirdparty/stb_image/include/stb_vorbis.c" + +#include "client/sound/SoundData.hpp" + +class SoundStream +{ +protected: + typedef unsigned int BufferId; + +protected: + float m_volume; + bool m_bIsStreaming; + bool m_bIsPaused; + bool m_bShouldLoop; + + stb_vorbis* m_decoder; + stb_vorbis_info m_info; + size_t m_totalSamplesLeft; + + SoundBuffer m_tempPcmBuffer; + +public: + SoundStream(); + virtual ~SoundStream(); + +private: + void _deleteDecoder(); + +protected: + virtual void _setVolume(float vol) = 0; + virtual void _play() = 0; + virtual void _pause() = 0; + virtual bool _open(const std::string& fileName) = 0; + virtual void _close() = 0; + virtual void _update() = 0; + virtual void _publishBuffer(unsigned int destBufferId, const SoundBuffer& sourceBuffer) = 0; + + bool _stream(int bufferId); + +public: + float getVolume() const { return m_volume; } + void setVolume(float vol); + bool isStreaming() const { return m_bIsStreaming; } + bool isPaused() const { return m_bIsPaused; } + void setPausedState(bool isPaused); + bool isPlaying() const { return isStreaming() && !isPaused(); } + bool shouldLoop() const { return m_bShouldLoop; } + + bool open(const std::string& fileName); + void close(); + void update(); +}; \ No newline at end of file diff --git a/source/client/sound/SoundSystem.cpp b/source/client/sound/SoundSystem.cpp index 9fb023458..81903adfa 100644 --- a/source/client/sound/SoundSystem.cpp +++ b/source/client/sound/SoundSystem.cpp @@ -17,31 +17,69 @@ bool SoundSystem::isAvailable() return false; } -void SoundSystem::setListenerPos(float x, float y, float z) +void SoundSystem::setListenerPos(const Vec3& pos) { } -void SoundSystem::setListenerAngle(float yaw, float pitch) +void SoundSystem::setListenerAngle(const Vec2& rot) { } -void SoundSystem::load(const std::string& sound) +void SoundSystem::setListenerVelocity(const Vec3& vel) { } -void SoundSystem::play(const std::string& sound) +void SoundSystem::setMusicVolume(float vol) { } -void SoundSystem::pause(const std::string& sound) +void SoundSystem::setSoundVolume(float vol) { } -void SoundSystem::stop(const std::string& sound) +void SoundSystem::load(const std::string& soundPath, bool is3D, float minDis) { } -void SoundSystem::playAt(const SoundDesc& sound, float x, float y, float z, float a, float b) +void SoundSystem::play(const std::string& soundPath) +{ +} + +void SoundSystem::pause(const std::string& soundPath) +{ +} + +void SoundSystem::stop(const std::string& soundPath) +{ +} + +void SoundSystem::playAt(const SoundDesc& sound, const Vec3& pos, float volume, float pitch) +{ +} + +void SoundSystem::playMusic(const std::string& soundPath) +{ +} + +bool SoundSystem::isPlayingMusic() const +{ + return false; +} + +bool SoundSystem::isPlayingMusic(const std::string& soundPath) const +{ + return false; +} + +void SoundSystem::stopMusic() +{ +} + +void SoundSystem::pauseMusic(bool state) +{ +} + +void SoundSystem::update(float elapsedTime) { } diff --git a/source/client/sound/SoundSystem.hpp b/source/client/sound/SoundSystem.hpp index 3192fa58d..c458f92b4 100644 --- a/source/client/sound/SoundSystem.hpp +++ b/source/client/sound/SoundSystem.hpp @@ -9,25 +9,48 @@ #pragma once #include +#include "world/phys/Vec2.hpp" +#include "world/phys/Vec3.hpp" #include "SoundData.hpp" +// Platform-agnostic sound settings // +// Just guessing for this one +#define SOUND_ATTENUATION_MIN_DISTANCE 2.0f +#define SOUND_MAX_DISTANCE 16.0f +// 28 non-streaming channels in Paulscode +// @NOTE: Currently only SoundSystemAL adheres to this. +#define SOUND_MAX_SOURCES 28 + class SoundSystem { public: virtual ~SoundSystem(); virtual bool isAvailable(); - virtual void setListenerPos(float x, float y, float z); - virtual void setListenerAngle(float yaw, float pitch); - virtual void load(const std::string& sound); - virtual void play(const std::string& sound); - virtual void pause(const std::string& sound); - virtual void stop(const std::string& sound); - virtual void playAt(const SoundDesc& sound, float x, float y, float z, float a, float b); + virtual void setListenerPos(const Vec3& pos); + virtual void setListenerAngle(const Vec2& rot); + virtual void setListenerVelocity(const Vec3& vel); + + virtual void setMusicVolume(float vol); + virtual void setSoundVolume(float vol); + + virtual void load(const std::string& soundPath, bool is3D, float minDis); + virtual void play(const std::string& soundPath); + virtual void pause(const std::string& soundPath); + virtual void stop(const std::string& soundPath); + virtual void playAt(const SoundDesc& sound, const Vec3& pos, float volume, float pitch); + + virtual void playMusic(const std::string& soundPath); + virtual bool isPlayingMusic() const; + virtual bool isPlayingMusic(const std::string& soundPath) const; + virtual void stopMusic(); + virtual void pauseMusic(bool state); + + virtual void update(float elapsedTime); // Be prepared for these to be called regardless of engine state - virtual void startEngine(); - virtual void stopEngine(); + virtual void startEngine(); // called init in 0.10.0 + virtual void stopEngine(); // called destroy in 0.10.0 virtual void muteAudio(); virtual void unMuteAudio(); diff --git a/source/client/sound/music_list.h b/source/client/sound/music_list.h new file mode 100644 index 000000000..b80142b9c --- /dev/null +++ b/source/client/sound/music_list.h @@ -0,0 +1,12 @@ +MUSIC(calm, 1) +MUSIC(calm, 2) +MUSIC(calm, 3) +NEWMUSIC(piano, 1) +NEWMUSIC(piano, 2) +NEWMUSIC(piano, 3) +NEWMUSIC(hal, 1) +NEWMUSIC(hal, 2) +NEWMUSIC(hal, 3) +NEWMUSIC(hal, 4) +NEWMUSIC(nuance, 1) +NEWMUSIC(nuance, 2) \ No newline at end of file diff --git a/source/client/sound/sound_list.h b/source/client/sound/sound_list.h new file mode 100644 index 000000000..70ae21ec2 --- /dev/null +++ b/source/client/sound/sound_list.h @@ -0,0 +1,67 @@ +SOUND(step, cloth, 1) +SOUND(step, cloth, 2) +SOUND(step, cloth, 3) +SOUND(step, cloth, 4) + +SOUND(step, grass, 1) +SOUND(step, grass, 2) +SOUND(step, grass, 3) +SOUND(step, grass, 4) + +SOUND(step, gravel, 1) +SOUND(step, gravel, 2) +SOUND(step, gravel, 3) +SOUND(step, gravel, 4) + +SOUND(step, sand, 1) +SOUND(step, sand, 2) +SOUND(step, sand, 3) +SOUND(step, sand, 4) + +SOUND(step, stone, 1) +SOUND(step, stone, 2) +SOUND(step, stone, 3) +SOUND(step, stone, 4) + +SOUND(step, wood, 1) +SOUND(step, wood, 2) +SOUND(step, wood, 3) +SOUND(step, wood, 4) + +SOUND(random, splash, ) +SOUND(random, explode, ) +SOUND(random, click, ) +SOUND(random, door_close, ) +SOUND(random, door_open, ) +SOUND(random, hurt, ) +SOUND(random, pop, ) +SOUND(random, fizz, ) + +SOUND(fire, fire, ) + +SOUND(damage, fallbig, 1) +SOUND(damage, fallbig, 2) + +SOUND(damage, fallsmall, ) + +SOUND(mob, pig, 1) +SOUND(mob, pig, 2) +SOUND(mob, pig, 3) +SOUND(mob, pigdeath, ) + +SOUND(mob, sheep, 1) +SOUND(mob, sheep, 2) +SOUND(mob, sheep, 3) + +SOUND(mob, cow, 1) +SOUND(mob, cow, 2) +SOUND(mob, cow, 3) +SOUND(mob, cow, 4) +SOUND(mob, cowhurt, 1) +SOUND(mob, cowhurt, 2) +SOUND(mob, cowhurt, 3) + +SOUND(mob, chicken, 2) +SOUND(mob, chicken, 3) +SOUND(mob, chickenhurt, 1) +SOUND(mob, chickenhurt, 2) \ No newline at end of file diff --git a/source/common/Mth.cpp b/source/common/Mth.cpp index e3fa9cef3..9b1b07242 100644 --- a/source/common/Mth.cpp +++ b/source/common/Mth.cpp @@ -84,12 +84,12 @@ unsigned Mth::fastRandom() return(x4 = x4 ^ (unsigned(x4) >> 19) ^ x0 ^ (x0 << 11) ^ ((x0 ^ unsigned(x0 << 11)) >> 8)); } -float Mth::clamp(float a, float min, float max) +float Mth::clamp(float x, float min, float max) { - if (a > max) + if (x > max) return max; - if (a > min) - return a; + if (x > min) + return x; else return min; return max; diff --git a/source/network/PacketUtil.cpp b/source/network/PacketUtil.cpp index 2ed6af551..268f1f211 100644 --- a/source/network/PacketUtil.cpp +++ b/source/network/PacketUtil.cpp @@ -19,10 +19,10 @@ void PacketUtil::Rot_entityToChar(const Entity* entity, char& yawChar, char& pit void PacketUtil::Rot_charToEntity(Entity* entity, char yawChar, char pitchChar) { float pitch = PacketUtil::Rot_charToDegrees(pitchChar); - entity->m_rotPrev.y = pitch; + entity->m_oRot.y = pitch; entity->m_rot.y = pitch; float yaw = PacketUtil::Rot_charToDegrees(yawChar); - entity->m_rotPrev.x = yaw; + entity->m_oRot.x = yaw; entity->m_rot.x = yaw; } \ No newline at end of file diff --git a/source/renderer/GL/GL.cpp b/source/renderer/GL/GL.cpp index 65c9c6eea..cb3b3d3e5 100644 --- a/source/renderer/GL/GL.cpp +++ b/source/renderer/GL/GL.cpp @@ -65,19 +65,19 @@ int glhProjectf(float objx, float objy, float objz, float* modelview, float* pro fTempo[6] = projection[2] * fTempo[0] + projection[6] * fTempo[1] + projection[10] * fTempo[2] + projection[14] * fTempo[3]; fTempo[7] = -fTempo[2]; // The result normalizes between -1 and 1 - if (fTempo[7] == 0.0) // The w value + if (fTempo[7] == 0.0f) // The w value return 0; - fTempo[7] = 1.0 / fTempo[7]; + fTempo[7] = 1.0f / fTempo[7]; // Perspective division fTempo[4] *= fTempo[7]; fTempo[5] *= fTempo[7]; fTempo[6] *= fTempo[7]; // Window coordinates // Map x, y to range 0-1 - windowCoordinate[0] = (fTempo[4] * 0.5 + 0.5) * viewport[2] + viewport[0]; - windowCoordinate[1] = (fTempo[5] * 0.5 + 0.5) * viewport[3] + viewport[1]; - // This is only correct when glDepthRange(0.0, 1.0) - windowCoordinate[2] = (1.0 + fTempo[6]) * 0.5; // Between 0 and 1 + windowCoordinate[0] = (fTempo[4] * 0.5f + 0.5f) * viewport[2] + viewport[0]; + windowCoordinate[1] = (fTempo[5] * 0.5f + 0.5f) * viewport[3] + viewport[1]; + // This is only correct when glDepthRange(0.0f, 1.0f) + windowCoordinate[2] = (1.0f + fTempo[6]) * 0.5f; // Between 0 and 1 return 1; } @@ -93,15 +93,15 @@ int glhUnProjectf(float winx, float winy, float winz, float* modelview, float* p if (glhInvertMatrixf2(A, m) == 0) return 0; // Transformation of normalized coordinates between -1 and 1 - in[0] = (winx - (float)viewport[0]) / (float)viewport[2] * 2.0 - 1.0; - in[1] = (winy - (float)viewport[1]) / (float)viewport[3] * 2.0 - 1.0; - in[2] = 2.0 * winz - 1.0; - in[3] = 1.0; + in[0] = (winx - (float)viewport[0]) / (float)viewport[2] * 2.0f - 1.0f; + in[1] = (winy - (float)viewport[1]) / (float)viewport[3] * 2.0f - 1.0f; + in[2] = 2.0f * winz - 1.0f; + in[3] = 1.0f; // Objects coordinates MultiplyMatrixByVector4by4OpenGL_FLOAT(out, m, in); - if (out[3] == 0.0) + if (out[3] == 0.0f) return 0; - out[3] = 1.0 / out[3]; + out[3] = 1.0f / out[3]; objectCoordinate[0] = out[0] * out[3]; objectCoordinate[1] = out[1] * out[3]; objectCoordinate[2] = out[2] * out[3]; @@ -197,16 +197,16 @@ int glhInvertMatrixf2(float* m, float* out) r0 = wtmp[0], r1 = wtmp[1], r2 = wtmp[2], r3 = wtmp[3]; r0[0] = MAT(m, 0, 0), r0[1] = MAT(m, 0, 1), r0[2] = MAT(m, 0, 2), r0[3] = MAT(m, 0, 3), - r0[4] = 1.0, r0[5] = r0[6] = r0[7] = 0.0, + r0[4] = 1.0f, r0[5] = r0[6] = r0[7] = 0.0f, r1[0] = MAT(m, 1, 0), r1[1] = MAT(m, 1, 1), r1[2] = MAT(m, 1, 2), r1[3] = MAT(m, 1, 3), - r1[5] = 1.0, r1[4] = r1[6] = r1[7] = 0.0, + r1[5] = 1.0f, r1[4] = r1[6] = r1[7] = 0.0f, r2[0] = MAT(m, 2, 0), r2[1] = MAT(m, 2, 1), r2[2] = MAT(m, 2, 2), r2[3] = MAT(m, 2, 3), - r2[6] = 1.0, r2[4] = r2[5] = r2[7] = 0.0, + r2[6] = 1.0f, r2[4] = r2[5] = r2[7] = 0.0f, r3[0] = MAT(m, 3, 0), r3[1] = MAT(m, 3, 1), r3[2] = MAT(m, 3, 2), r3[3] = MAT(m, 3, 3), - r3[7] = 1.0, r3[4] = r3[5] = r3[6] = 0.0; + r3[7] = 1.0f, r3[4] = r3[5] = r3[6] = 0.0f; /* choose pivot - or die */ if (fabsf(r3[0]) > fabsf(r2[0])) SWAP_ROWS_FLOAT(r3, r2); @@ -214,7 +214,7 @@ int glhInvertMatrixf2(float* m, float* out) SWAP_ROWS_FLOAT(r2, r1); if (fabsf(r1[0]) > fabsf(r0[0])) SWAP_ROWS_FLOAT(r1, r0); - if (0.0 == r0[0]) + if (0.0f == r0[0]) return 0; /* eliminate first variable */ m1 = r1[0] / r0[0]; @@ -233,25 +233,25 @@ int glhInvertMatrixf2(float* m, float* out) r2[3] -= m2 * s; r3[3] -= m3 * s; s = r0[4]; - if (s != 0.0) { + if (s != 0.0f) { r1[4] -= m1 * s; r2[4] -= m2 * s; r3[4] -= m3 * s; } s = r0[5]; - if (s != 0.0) { + if (s != 0.0f) { r1[5] -= m1 * s; r2[5] -= m2 * s; r3[5] -= m3 * s; } s = r0[6]; - if (s != 0.0) { + if (s != 0.0f) { r1[6] -= m1 * s; r2[6] -= m2 * s; r3[6] -= m3 * s; } s = r0[7]; - if (s != 0.0) { + if (s != 0.0f) { r1[7] -= m1 * s; r2[7] -= m2 * s; r3[7] -= m3 * s; @@ -261,7 +261,7 @@ int glhInvertMatrixf2(float* m, float* out) SWAP_ROWS_FLOAT(r3, r2); if (fabsf(r2[1]) > fabsf(r1[1])) SWAP_ROWS_FLOAT(r2, r1); - if (0.0 == r1[1]) + if (0.0f == r1[1]) return 0; /* eliminate second variable */ m2 = r2[1] / r1[1]; @@ -271,44 +271,44 @@ int glhInvertMatrixf2(float* m, float* out) r2[3] -= m2 * r1[3]; r3[3] -= m3 * r1[3]; s = r1[4]; - if (0.0 != s) { + if (0.0f != s) { r2[4] -= m2 * s; r3[4] -= m3 * s; } s = r1[5]; - if (0.0 != s) { + if (0.0f != s) { r2[5] -= m2 * s; r3[5] -= m3 * s; } s = r1[6]; - if (0.0 != s) { + if (0.0f != s) { r2[6] -= m2 * s; r3[6] -= m3 * s; } s = r1[7]; - if (0.0 != s) { + if (0.0f != s) { r2[7] -= m2 * s; r3[7] -= m3 * s; } /* choose pivot - or die */ if (fabsf(r3[2]) > fabsf(r2[2])) SWAP_ROWS_FLOAT(r3, r2); - if (0.0 == r2[2]) + if (0.0f == r2[2]) return 0; /* eliminate third variable */ m3 = r3[2] / r2[2]; r3[3] -= m3 * r2[3], r3[4] -= m3 * r2[4], r3[5] -= m3 * r2[5], r3[6] -= m3 * r2[6], r3[7] -= m3 * r2[7]; /* last check */ - if (0.0 == r3[3]) + if (0.0f == r3[3]) return 0; - s = 1.0 / r3[3]; /* now back substitute row 3 */ + s = 1.0f / r3[3]; /* now back substitute row 3 */ r3[4] *= s; r3[5] *= s; r3[6] *= s; r3[7] *= s; m2 = r2[3]; /* now back substitute row 2 */ - s = 1.0 / r2[2]; + s = 1.0f / r2[2]; r2[4] = s * (r2[4] - r3[4] * m2), r2[5] = s * (r2[5] - r3[5] * m2), r2[6] = s * (r2[6] - r3[6] * m2), r2[7] = s * (r2[7] - r3[7] * m2); m1 = r1[3]; @@ -318,14 +318,14 @@ int glhInvertMatrixf2(float* m, float* out) r0[4] -= r3[4] * m0, r0[5] -= r3[5] * m0, r0[6] -= r3[6] * m0, r0[7] -= r3[7] * m0; m1 = r1[2]; /* now back substitute row 1 */ - s = 1.0 / r1[1]; + s = 1.0f / r1[1]; r1[4] = s * (r1[4] - r2[4] * m1), r1[5] = s * (r1[5] - r2[5] * m1), r1[6] = s * (r1[6] - r2[6] * m1), r1[7] = s * (r1[7] - r2[7] * m1); m0 = r0[2]; r0[4] -= r2[4] * m0, r0[5] -= r2[5] * m0, r0[6] -= r2[6] * m0, r0[7] -= r2[7] * m0; m0 = r0[1]; /* now back substitute row 0 */ - s = 1.0 / r0[0]; + s = 1.0f / r0[0]; r0[4] = s * (r0[4] - r1[4] * m0), r0[5] = s * (r0[5] - r1[5] * m0), r0[6] = s * (r0[6] - r1[6] * m0), r0[7] = s * (r0[7] - r1[7] * m0); MAT(out, 0, 0) = r0[4]; diff --git a/source/world/entity/Entity.cpp b/source/world/entity/Entity.cpp index 4d08af3e8..1359b7690 100644 --- a/source/world/entity/Entity.cpp +++ b/source/world/entity/Entity.cpp @@ -26,7 +26,7 @@ void Entity::_init() m_bBlocksBuilding = false; m_pLevel = nullptr; m_rot = Vec2::ZERO; - m_rotPrev = Vec2::ZERO; + m_oRot = Vec2::ZERO; m_onGround = false; m_bHorizontalCollision = false; field_7E = false; @@ -45,7 +45,7 @@ void Entity::_init() m_bNoPhysics = false; field_B0 = 0.0f; m_tickCount = 0; - field_B8 = 0; + m_invulnerableTime = 0; m_airCapacity = TOTAL_AIR_SUPPLY; m_fireTicks = 0; m_flameTime = 1; @@ -439,15 +439,14 @@ int Entity::move(const Vec3& pos) bool bPlaySound = true; const Tile::SoundType *sound = Tile::tiles[tileID]->m_pSound; - /*if (!isPlayer()) // no idea why this wasn't already a thing - bPlaySound = false;*/ if (m_pLevel->getTile(tilePos.above()) == Tile::topSnow->m_ID) sound = Tile::topSnow->m_pSound; else if (Tile::tiles[tileID]->m_pMaterial->isLiquid()) bPlaySound = false; + // vol is * 0.15f in Java, is quiet for whatever reason, so bumping to 0.20f if (bPlaySound) - m_pLevel->playSound(this, "step." + sound->m_name, sound->volume * 0.15f, sound->pitch); + m_pLevel->playSound(this, "step." + sound->m_name, sound->volume * 0.20f, sound->pitch); Tile::tiles[tileID]->stepOn(m_pLevel, tilePos, this); } @@ -516,18 +515,18 @@ void Entity::absMoveTo(const Vec3& pos, const Vec2& rot) { m_ySlideOffset = 0.0f; - m_rotPrev = rot; + m_oRot = rot; setRot(rot); setPos(pos); m_oPos = pos; // This looks like a rebounding check for the angle - float dyRot = (m_rotPrev.y - m_rot.y); + float dyRot = (m_oRot.y - m_rot.y); if (dyRot < -180.0f) - m_rotPrev.y += 360.0f; + m_oRot.y += 360.0f; if (dyRot >= 180.0f) - m_rotPrev.y -= 360.0f; + m_oRot.y -= 360.0f; } void Entity::moveRelative(const Vec3& pos) @@ -575,15 +574,15 @@ void Entity::turn(const Vec2& rot) interpolateTurn(rot); - m_rotPrev.x += m_rot.x - rotOld.x; - m_rotPrev.y += m_rot.y - rotOld.y; + m_oRot.x += m_rot.x - rotOld.x; + m_oRot.y += m_rot.y - rotOld.y; } void Entity::reset() { // TODO is this it m_posPrev = m_oPos = m_pos; - m_rotPrev = m_rot; + m_oRot = m_rot; m_bRemoved = false; m_distanceFallen = 0.0f; field_D5 = false; @@ -616,7 +615,7 @@ void Entity::baseTick() field_90 = m_walkDist; m_oPos = m_pos; m_tickCount++; - m_rotPrev = m_rot; + m_oRot = m_rot; if (isInWater()) { if (!field_D4 && !field_D6) diff --git a/source/world/entity/Entity.hpp b/source/world/entity/Entity.hpp index 520eae1af..c64816a2b 100644 --- a/source/world/entity/Entity.hpp +++ b/source/world/entity/Entity.hpp @@ -183,14 +183,10 @@ class Entity float field_30; bool m_bBlocksBuilding; Level* m_pLevel; - Vec3 m_oPos; // "o" in Java or "xo" ""yo" "zo" + Vec3 m_oPos; // "o" in Java or "xo" "yo" "zo" Vec3 m_vel; Vec2 m_rot; - //maybe these are the actual m_yaw and m_pitch, and - //the one I annotated are the destination yaw and pitch. - //interpolateTurn doesn't modify them, so I highly suspect - //this to be the case. - Vec2 m_rotPrev; + Vec2 m_oRot; // "RotO" in Java or "xRotO" "yRotO" AABB m_hitbox; bool m_onGround; bool m_bHorizontalCollision; @@ -210,7 +206,7 @@ class Entity bool m_bNoPhysics; float field_B0; int m_tickCount; - int field_B8; + int m_invulnerableTime; int m_airCapacity; int m_fireTicks; int m_flameTime; diff --git a/source/world/entity/ItemEntity.cpp b/source/world/entity/ItemEntity.cpp index 9dfa8b7cd..3cf2772a0 100644 --- a/source/world/entity/ItemEntity.cpp +++ b/source/world/entity/ItemEntity.cpp @@ -73,7 +73,7 @@ void ItemEntity::playerTouch(Player* player) pInventory->addItem(&m_itemInstance); m_pLevel->playSound(this, "random.pop", 0.3f, - (((sharedRandom.nextFloat() - sharedRandom.nextFloat()) * 0.7f) + 1.0f) + (((sharedRandom.nextFloat() - sharedRandom.nextFloat()) * 0.7f) + 1.0f)); + ((sharedRandom.nextFloat() - sharedRandom.nextFloat()) * 0.7f + 1.0f) * 2.0f); if (m_itemInstance.m_count <= 0) remove(); diff --git a/source/world/entity/Mob.cpp b/source/world/entity/Mob.cpp index 3b39b78d7..7b932cd54 100644 --- a/source/world/entity/Mob.cpp +++ b/source/world/entity/Mob.cpp @@ -12,14 +12,14 @@ Mob::Mob(Level* pLevel) : Entity(pLevel) { - field_DC = 10; + m_invulnerableDuration = 10; field_E8 = 0.0f; field_EC = 0.0f; field_F0 = 0; m_oAttackAnim = 0.0f; m_attackAnim = 0.0f; m_health = 10; - field_100 = 20; + m_lastHealth = 20; m_hurtTime = 0; m_hurtDuration = 0; m_hurtDir = 0.0f; @@ -30,12 +30,12 @@ Mob::Mob(Level* pLevel) : Entity(pLevel) field_120 = 0; field_124 = 0; field_128 = 0.0f; - field_12C = 0.0f; + m_walkAnimSpeed = 0.0f; field_130 = 0.0f; m_noActionTime = 0; field_B00 = Vec2::ZERO; field_B08 = 0.0f; - m_bJumping = 0; + m_bJumping = false; field_B10 = 0; m_runSpeed = 0.7f; field_B48 = 0; @@ -51,10 +51,11 @@ Mob::Mob(Level* pLevel) : Entity(pLevel) m_lSteps = 0; m_lPos = Vec3::ZERO; m_lRot = Vec2::ZERO; - field_B84 = 0; + m_lastHurt = 0; m_pEntLookedAt = nullptr; m_bSwinging = false; m_swingTime = 0; + m_ambientSoundTime = 0; m_texture = "/mob/pig.png"; m_class = ""; @@ -201,11 +202,11 @@ void Mob::tick() // Similar to rotlerp // I'm pretty sure this is super inefficient and its trying to do what I have it doing in setRot already. - while (x4 - m_rotPrev.x < -180.0f) - m_rotPrev.x -= 360.0f; + while (x4 - m_oRot.x < -180.0f) + m_oRot.x -= 360.0f; - while (x4 - m_rotPrev.x >= 180.0f) - m_rotPrev.x += 360.0f; + while (x4 - m_oRot.x >= 180.0f) + m_oRot.x += 360.0f; while (field_E8 - field_EC < -180.0f) field_EC -= 360.0f; @@ -213,84 +214,96 @@ void Mob::tick() while (field_E8 - field_EC >= 180.0f) field_EC += 360.0f; - while (m_rot.y - m_rotPrev.y < -180.0f) - m_rotPrev.y -= 360.0f; + while (m_rot.y - m_oRot.y < -180.0f) + m_oRot.y -= 360.0f; - while (m_rot.y - m_rotPrev.y >= 180.0f) - m_rotPrev.y += 360.0f; + while (m_rot.y - m_oRot.y >= 180.0f) + m_oRot.y += 360.0f; field_B54 += x2; } void Mob::baseTick() { - m_oAttackAnim = m_attackAnim; - Entity::baseTick(); - - if (isAlive() && isInWall()) - hurt(nullptr, 1); - - if (isAlive() && isUnderLiquid(Material::water) && !isWaterMob()) - { - m_airCapacity--; - if (m_airCapacity == -20) - { - m_airCapacity = 0; - - for (int i = 0; i < 8; i++) - { - m_pLevel->addParticle("bubble", - Vec3( - m_pos.x + m_random.nextFloat() - m_random.nextFloat(), - m_pos.y + m_random.nextFloat() - m_random.nextFloat(), - m_pos.z + m_random.nextFloat() - m_random.nextFloat() - ), - m_vel - ); - } - - hurt(nullptr, 2); - } - } - else - { - m_airCapacity = m_airSupply; - } + m_oAttackAnim = m_attackAnim; + Entity::baseTick(); + + if (m_random.nextInt(1000) < m_ambientSoundTime++) + { + playAmbientSound(); + } + + if (isAlive() && isInWall()) + hurt(nullptr, 1); + + // Java + /*if (m_bFireImmune || m_pLevel->m_bIsMultiplayer) + { + m_fireTicks = 0; + }*/ + + + if (isAlive() && isUnderLiquid(Material::water) && !isWaterMob()) + { + m_airCapacity--; + if (m_airCapacity == -20) + { + m_airCapacity = 0; + + for (int i = 0; i < 8; i++) + { + m_pLevel->addParticle("bubble", + Vec3( + m_pos.x + m_random.nextFloat() - m_random.nextFloat(), + m_pos.y + m_random.nextFloat() - m_random.nextFloat(), + m_pos.z + m_random.nextFloat() - m_random.nextFloat() + ), + m_vel + ); + } + + hurt(nullptr, 2); + } + } + else + { + m_airCapacity = m_airSupply; + } m_oTilt = m_tilt; - if (m_attackTime > 0) m_attackTime--; - if (m_hurtTime > 0) m_hurtTime--; - if (field_B8 > 0) field_B8--; - - if (m_health <= 0) - { - field_110++; - if (field_110 > 20) - { - beforeRemove(); - remove(); - for (int i = 0; i < 20; i++) - { - m_pLevel->addParticle("explode", - Vec3( - m_pos.x + 2 * m_bbWidth * m_random.nextFloat() - m_bbWidth, - m_pos.y + m_bbHeight * m_random.nextFloat(), - m_pos.z + 2 * m_bbWidth * m_random.nextFloat() - m_bbWidth - ), - Vec3( - 0.02f * (m_random.nextFloat() * 2 - 1) * (m_random.nextFloat() * 2 - 1), - 0.02f * (m_random.nextFloat() * 2 - 1) * (m_random.nextFloat() * 2 - 1), - 0.02f * (m_random.nextFloat() * 2 - 1) * (m_random.nextFloat() * 2 - 1) - ) - ); - } - } - } - - field_B58 = field_B54; - field_EC = field_E8; - m_rotPrev = m_rot; + if (m_attackTime > 0) m_attackTime--; + if (m_hurtTime > 0) m_hurtTime--; + if (m_invulnerableTime > 0) m_invulnerableTime--; + + if (m_health <= 0) + { + field_110++; + if (field_110 > 20) + { + beforeRemove(); + remove(); + for (int i = 0; i < 20; i++) + { + m_pLevel->addParticle("explode", + Vec3( + m_pos.x + 2 * m_bbWidth * m_random.nextFloat() - m_bbWidth, + m_pos.y + m_bbHeight * m_random.nextFloat(), + m_pos.z + 2 * m_bbWidth * m_random.nextFloat() - m_bbWidth + ), + Vec3( + 0.02f * (m_random.nextFloat() * 2 - 1) * (m_random.nextFloat() * 2 - 1), + 0.02f * (m_random.nextFloat() * 2 - 1) * (m_random.nextFloat() * 2 - 1), + 0.02f * (m_random.nextFloat() * 2 - 1) * (m_random.nextFloat() * 2 - 1) + ) + ); + } + } + } + + field_B58 = field_B54; + field_EC = field_E8; + m_oRot = m_rot; } bool Mob::isAlive() const @@ -303,58 +316,75 @@ bool Mob::isAlive() const bool Mob::hurt(Entity *pAttacker, int damage) { - if (m_pLevel->m_bIsMultiplayer) - return false; - - m_noActionTime = 0; - - if (m_health <= 0) - return false; - - field_12C = 1.5f; - if (float(field_B8) <= float(field_DC) * 0.5f) - { - field_100 = m_health; - field_B8 = field_DC; - field_B84 = damage; - actuallyHurt(damage); - m_hurtDuration = 10; - m_hurtTime = 10; - - // not in 0.1 - markHurt(); - - if (pAttacker) - { - float xd = pAttacker->m_pos.x - m_pos.x; - float zd = pAttacker->m_pos.z - m_pos.z; - - while (zd * zd + xd * xd < 0.0001f) - { - xd = 0.01f * (Mth::random() - Mth::random()); - zd = 0.01f * (Mth::random() - Mth::random()); - } - - float ang = atan2f(zd, xd); - v020_field_104 = ang * (180.0f / float(M_PI)) - m_rot.x; - - knockback(pAttacker, damage, xd, zd); - } - } - else - { - if (field_B84 >= damage) - return 0; - - actuallyHurt(damage - field_B84); - field_B84 = damage; - } - - m_hurtDir = 0; - if (m_health <= 0) - die(pAttacker); - - return true; + if (m_pLevel->m_bIsMultiplayer) + return false; + + m_noActionTime = 0; + + if (m_health <= 0) + return false; + + m_walkAnimSpeed = 1.5f; + bool var3 = true; + if (float(m_invulnerableTime) > float(m_invulnerableDuration) / 2.0f) + { + if (damage <= m_lastHurt) + return false; + + actuallyHurt(damage - m_lastHurt); + m_lastHurt = damage; + var3 = false; + } + else + { + m_lastHurt = damage; + m_lastHealth = m_health; + m_invulnerableTime = m_invulnerableDuration; + actuallyHurt(damage); + m_hurtTime = m_hurtDuration = 10; + } + + m_hurtDir = 0.0f; + // not in 0.1 + if (var3) + { + //m_pLevel->broadcastEntityEvent(this, 2); // Java + markHurt(); + + if (pAttacker) + { + float xd = pAttacker->m_pos.x - m_pos.x; + float zd = pAttacker->m_pos.z - m_pos.z; + + while (zd * zd + xd * xd < 0.0001f) + { + xd = 0.01f * (Mth::random() - Mth::random()); + zd = 0.01f * (Mth::random() - Mth::random()); + } + + float ang = atan2f(zd, xd); + v020_field_104 = ang * (180.0f / float(M_PI)) - m_rot.x; + + knockback(pAttacker, damage, xd, zd); + } + } + + if (m_health <= 0) + { + if (var3) + { + m_pLevel->playSound(this, getDeathSound(), getSoundVolume(), (m_random.nextFloat() - m_random.nextFloat()) * 0.2f + 1.0f); + } + + die(pAttacker); + } + else if (var3) + { + m_pLevel->playSound(this, getHurtSound(), getSoundVolume(), (m_random.nextFloat() - m_random.nextFloat()) * 0.2f + 1.0f); + } + + + return true; } void Mob::animateHurt() @@ -390,9 +420,13 @@ void Mob::causeFallDamage(float level) hurt(nullptr, x); - //@HUH: useless call to getTile? or could this be a return value of some sort - //Entity::causeFallDamage returns nothing though, so.... - m_pLevel->getTile(TilePos(m_pos.x, m_pos.y - 0.2f - m_heightOffset, m_pos.z)); + TileID tileId = m_pLevel->getTile(TilePos(m_pos.x, m_pos.y - 0.2f - m_heightOffset, m_pos.z)); + if (tileId > 0) + { + const Tile::SoundType* pSound = Tile::tiles[tileId]->m_pSound; + + m_pLevel->playSound(this, "step." + pSound->m_name, pSound->volume * 0.5f, pSound->pitch * 0.75f); + } } } @@ -432,7 +466,12 @@ std::string Mob::getTexture() const void Mob::playAmbientSound() { - + m_ambientSoundTime = -getAmbientSoundInterval(); + std::string sound = getAmbientSound(); + if (sound != "") + { + m_pLevel->playSound(this, sound, getSoundVolume(), (m_random.nextFloat() - m_random.nextFloat()) * 0.2f + 1.0f); + } } int Mob::getAmbientSoundInterval() const @@ -449,7 +488,7 @@ void Mob::heal(int health) if (m_health > C_MAX_MOB_HEALTH) m_health = C_MAX_MOB_HEALTH; - field_B8 = field_DC / 2; + m_invulnerableTime = m_invulnerableDuration / 2; } HitResult Mob::pick(float f1, float f2) @@ -575,7 +614,7 @@ bool Mob::canSee(Entity* pEnt) const void Mob::updateWalkAnim() { - field_128 = field_12C; + field_128 = m_walkAnimSpeed; float diffX = m_pos.x - m_oPos.x; float diffZ = m_pos.z - m_oPos.z; @@ -584,8 +623,8 @@ void Mob::updateWalkAnim() if (spd > 1.0f) spd = 1.0f; - field_12C += (spd - field_12C) * 0.4f; - field_130 += field_12C; + m_walkAnimSpeed += (spd - m_walkAnimSpeed) * 0.4f; + field_130 += m_walkAnimSpeed; } void Mob::aiStep() @@ -669,10 +708,18 @@ Vec3 Mob::getPos(float f) const ); } +Vec2 Mob::getRot(float f) const +{ + return Vec2( + Mth::Lerp(m_oRot.x, m_rot.x, f), + Mth::Lerp(m_oRot.y, m_rot.y, f) + ); +} + Vec3 Mob::getViewVector(float f) const { constexpr float C_180_OVER_PI = 0.017453f; - constexpr float C_PI = 3.1416f; + constexpr float C_PI = 3.1416f; // @HUH: Why not just use M_PI here? if (f == 1.0) { @@ -683,8 +730,8 @@ Vec3 Mob::getViewVector(float f) const return Vec3(x.x * x.z, Mth::sin(-(m_rot.y * C_180_OVER_PI)), x.y * x.z); } - float x1 = m_rotPrev.y + (m_rot.y - m_rotPrev.y) * f; - float x2 = -((m_rotPrev.x + (m_rot.x - m_rotPrev.x) * f) * C_180_OVER_PI) - C_PI; + float x1 = m_oRot.y + (m_rot.y - m_oRot.y) * f; + float x2 = -((m_oRot.x + (m_rot.x - m_oRot.x) * f) * C_180_OVER_PI) - C_PI; float x3 = Mth::cos(x2); float x4 = Mth::sin(x2); float x5 = -(x1 * C_180_OVER_PI); diff --git a/source/world/entity/Mob.hpp b/source/world/entity/Mob.hpp index 74d9abe65..cef53ef2e 100644 --- a/source/world/entity/Mob.hpp +++ b/source/world/entity/Mob.hpp @@ -59,6 +59,7 @@ class Mob : public Entity virtual bool canSpawn(); virtual float getAttackAnim(float f) const; virtual Vec3 getPos(float f) const; + virtual Vec2 getRot(float f) const; virtual Vec3 getLookAngle(float f) const { return getViewVector(1.0f); } virtual Vec3 getViewVector(float f) const; virtual int getMaxSpawnClusterSize() const { return 4; } @@ -83,9 +84,12 @@ class Mob : public Entity float rotlerp(float, float, float); void updateAttackAnim(); + +private: + int m_ambientSoundTime; public: - int field_DC; + int m_invulnerableDuration; float field_E0; float field_E4; float field_E8; @@ -94,7 +98,7 @@ class Mob : public Entity float m_oAttackAnim; float m_attackAnim; int m_health; - int field_100; + int m_lastHealth; int m_hurtTime; int m_hurtDuration; float m_hurtDir; @@ -105,7 +109,7 @@ class Mob : public Entity int field_120; int field_124; float field_128; - float field_12C; + float m_walkAnimSpeed; float field_130; Random m_random; int m_noActionTime; @@ -129,7 +133,7 @@ class Mob : public Entity int m_lSteps; Vec3 m_lPos; Vec2 m_lRot; - int field_B84; + int m_lastHurt; Entity* m_pEntLookedAt; float v020_field_104; diff --git a/source/world/entity/Player.cpp b/source/world/entity/Player.cpp index 937b83b53..d995bf56e 100644 --- a/source/world/entity/Player.cpp +++ b/source/world/entity/Player.cpp @@ -66,29 +66,32 @@ bool Player::hurt(Entity* pEnt, int damage) return false; } - EntityTypeDescriptor entDesc = pEnt->getDescriptor(); - - if (entDesc.hasCategory(EntityCategories::MONSTER) || entDesc.hasCategory(EntityCategories::ABSTRACT_ARROW)) - { - switch (m_pLevel->m_difficulty) - { - case 0: - damage = 0; - break; - case 1: - damage = damage / 3 + 1; - break; - case 2: - // Don't modify damage - break; - case 3: - damage = damage * 3 / 2; - break; - default: - assert(!"Unknown difficulty value"); - break; - } - } + if (pEnt != nullptr) + { + EntityTypeDescriptor entDesc = pEnt->getDescriptor(); + + if (entDesc.hasCategory(EntityCategories::MONSTER) || entDesc.hasCategory(EntityCategories::ABSTRACT_ARROW)) + { + switch (m_pLevel->m_difficulty) + { + case 0: + damage = 0; + break; + case 1: + damage = damage / 3 + 1; + break; + case 2: + // Don't modify damage + break; + case 3: + damage = damage * 3 / 2; + break; + default: + assert(!"Unknown difficulty value"); + break; + } + } + } return damage == 0 ? false : Mob::hurt(pEnt, damage); } diff --git a/source/world/entity/TripodCamera.cpp b/source/world/entity/TripodCamera.cpp index df00627d9..eb5fcc171 100644 --- a/source/world/entity/TripodCamera.cpp +++ b/source/world/entity/TripodCamera.cpp @@ -19,7 +19,7 @@ TripodCamera::TripodCamera(Level* level, Player* player, const Vec3& pos) : Mob( m_owner = player; field_C8 = RENDER_CAMERA; - m_rotPrev = m_rot = player->m_rot; + m_oRot = m_rot = player->m_rot; m_bBlocksBuilding = true; diff --git a/source/world/gamemode/CreativeMode.cpp b/source/world/gamemode/CreativeMode.cpp index f5f7a4c32..fbd53dd03 100644 --- a/source/world/gamemode/CreativeMode.cpp +++ b/source/world/gamemode/CreativeMode.cpp @@ -55,6 +55,7 @@ void CreativeMode::stopDestroyBlock() void CreativeMode::tick() { m_lastDestroyProgress = m_destroyProgress; + GameMode::tick(); } void CreativeMode::render(float f) diff --git a/source/world/gamemode/GameMode.cpp b/source/world/gamemode/GameMode.cpp index 410e37fad..e28e5f7e0 100644 --- a/source/world/gamemode/GameMode.cpp +++ b/source/world/gamemode/GameMode.cpp @@ -69,6 +69,8 @@ void GameMode::stopDestroyBlock() void GameMode::tick() { + // @NOTE: should only be in SurvivalMode & MultiPlayerGameMode, but Minecraft music is awesome + m_pMinecraft->m_pSoundEngine->playMusicTick(); } void GameMode::render(float f) diff --git a/source/world/gamemode/SurvivalMode.cpp b/source/world/gamemode/SurvivalMode.cpp index 54c5bf1d6..2f2c42481 100644 --- a/source/world/gamemode/SurvivalMode.cpp +++ b/source/world/gamemode/SurvivalMode.cpp @@ -132,6 +132,8 @@ void SurvivalMode::stopDestroyBlock() void SurvivalMode::tick() { m_lastDestroyProgress = m_destroyProgress; + //m_pMinecraft->m_pSoundEngine->playMusicTick(); // also on MultiPlayerGameMode + GameMode::tick(); } void SurvivalMode::render(float f) diff --git a/source/world/level/Level.cpp b/source/world/level/Level.cpp index 2e21dc644..084d50ffd 100644 --- a/source/world/level/Level.cpp +++ b/source/world/level/Level.cpp @@ -1396,7 +1396,7 @@ void Level::tick(Entity* pEnt, bool b) } pEnt->m_posPrev = pEnt->m_pos; - pEnt->m_rotPrev = pEnt->m_rot; + pEnt->m_oRot = pEnt->m_rot; if (pEnt->m_bInAChunk) pEnt->tick(); @@ -1404,7 +1404,7 @@ void Level::tick(Entity* pEnt, bool b) else { pEnt->m_posPrev = pEnt->m_pos; - pEnt->m_rotPrev = pEnt->m_rot; + pEnt->m_oRot = pEnt->m_rot; } ChunkPos cp(pEnt->m_pos); diff --git a/source/world/level/storage/LevelData.cpp b/source/world/level/storage/LevelData.cpp index 83f431e3b..aa16440e1 100644 --- a/source/world/level/storage/LevelData.cpp +++ b/source/world/level/storage/LevelData.cpp @@ -82,7 +82,7 @@ void PlayerData::loadPlayer(Player* player) // Let the setter do the validation player->setRot( - player->m_rotPrev = m_rot + player->m_oRot = m_rot ); player->m_distanceFallen = m_distanceFallen; player->m_fireTicks = field_24; diff --git a/thirdparty/GL/GL.hpp b/thirdparty/GL/GL.hpp index 2954eb6c1..abbe0c808 100644 --- a/thirdparty/GL/GL.hpp +++ b/thirdparty/GL/GL.hpp @@ -83,7 +83,7 @@ static inline void gluPerspective(GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar) { GLfloat m[4][4]; float sine, cotangent, deltaZ; - float radians = fovy / 2 * M_PI / 180; + float radians = fovy / 2.0f * M_PI / 180.0f; deltaZ = zFar - zNear; sine = sin(radians); diff --git a/thirdparty/OpenAL.h b/thirdparty/OpenAL.h new file mode 100644 index 000000000..b77806f64 --- /dev/null +++ b/thirdparty/OpenAL.h @@ -0,0 +1,24 @@ +#ifdef _WIN32 +#include +#include +#pragma comment( lib, "OpenAl32.lib" ) +#elif defined(__APPLE__) +#include +#include +#else +#include +#include +#endif + +// Error Checking +#define AL_ERROR_CHECK() AL_ERROR_CHECK_MANUAL(alGetError()) +#define AL_ERROR_CHECK_MANUAL(val) \ + { \ + ALenum __err = val; \ + if (__err != AL_NO_ERROR) \ + { \ + LOG_E("(%s:%i) OpenAL Error: %s", __FILE__, __LINE__, alGetString(__err)); \ + assert(!"An OpenAL error occurred!"); \ + } \ + } +#define AL_ERROR_SILENCE() { while ( alGetError() ); } \ No newline at end of file diff --git a/tools/extract_apk.py b/tools/extract_apk.py new file mode 100755 index 000000000..21f8c8764 --- /dev/null +++ b/tools/extract_apk.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +import setup_venv + +import grabsounds +import sys +import zipfile +import os + +# Constants +TITLE = 'APK Extractor' +ASSETS_DIR = 'assets/' +SOUND_DATA_FILE = 'libminecraftpe.so' + +# Determine Mode +use_gui = len(sys.argv) < 2 +if use_gui: + # Setup GUI + import tkinter as tk + from tkinter import filedialog, messagebox + root = tk.Tk() + root.withdraw() + +# Show Information +def info(msg): + print(msg) + if use_gui: + messagebox.showinfo(title=TITLE, message=msg) + +# Extract Sounds +def extract_sounds(apk_zip): + # Search For File + for name in apk_zip.namelist(): + if os.path.basename(name) == SOUND_DATA_FILE: + with apk_zip.open(name) as sound_data: + grabsounds.main(sound_data) + return + raise Exception('Unable to find sound data!') + +# Extract Assets +def extract_assets(apk_zip): + assets = os.path.dirname(os.path.abspath(__file__)) + assets = os.path.join(assets, '..', 'game', 'assets') + # Search For Assets + for name in apk_zip.namelist(): + if name.startswith(ASSETS_DIR): + # Found Asset + short_name = name[len(ASSETS_DIR):] + print(f'Extracting Asset {short_name}...') + out = os.path.join(assets, short_name) + os.makedirs(os.path.dirname(out), exist_ok=True) + # Extract + with open(out, 'wb') as file: + file.write(apk_zip.read(name)) + +# Open APK +def open_apk(): + if use_gui: + out = filedialog.askopenfile('rb', title=TITLE, filetypes=[('APK', '*.apk')]) + if out is None: + sys.exit() + return out + else: + return open(sys.argv[1], 'rb') + +# Main +def main(): + with open_apk() as apk: + # Extract + with zipfile.ZipFile(apk) as apk_zip: + extract_sounds(apk_zip) + extract_assets(apk_zip) + # Done + info('Done!') + +# Handle Errors +try: + main() +except Exception as e: + if use_gui: + info('An error has occurred!') + raise diff --git a/tools/grabsounds.py b/tools/grabsounds.py index 42bacb235..9040bc8fc 100755 --- a/tools/grabsounds.py +++ b/tools/grabsounds.py @@ -1,155 +1,36 @@ -#!/usr/bin/python3 +#!/usr/bin/env python3 +import setup_venv -# Minecraft PE Reverse Engineering Project -# Copyright (C) 2023 iProgramInCpp -# ----------------------------------------------- -# This tool grabs the sound data from the .so file from the source Minecraft PE -# Android *.so file. Offsets are hardcoded to that version, nothing else will work. -# -# TODO: Lookup ELF symbols to un-hardcode the offsets? +import lief import sys import os -import hashlib -# use memory efficient range always -merange = range -try: - merange = xrange # python 2 -except NameError: - pass - -# defines -pcm_addresses = [ - ('PCM_cloth1', 0x00112B08), - ('PCM_cloth2', 0x00117134), - ('PCM_cloth3', 0x0011B760), - ('PCM_cloth4', 0x0011FD8C), - ('PCM_grass1', 0x001243B8), - ('PCM_grass2', 0x0012F00C), - ('PCM_grass3', 0x00139C60), - ('PCM_grass4', 0x001448B4), - ('PCM_gravel1', 0x0014F508), - ('PCM_gravel2', 0x0015A15C), - ('PCM_gravel3', 0x00164DB0), - ('PCM_gravel4', 0x0016FA04), - ('PCM_sand1', 0x0017A658), - ('PCM_sand2', 0x00181214), - ('PCM_sand3', 0x00187DD0), - ('PCM_sand4', 0x0018E98C), - ('PCM_stone1', 0x00195548), - ('PCM_stone2', 0x001A019C), - ('PCM_stone3', 0x001AADF0), - ('PCM_stone4', 0x001B5A44), - ('PCM_wood1', 0x001C0698), - ('PCM_wood2', 0x001CB2EC), - ('PCM_wood3', 0x001D5F40), - ('PCM_wood4', 0x001E0B94), - ('PCM_click', 0x001EB7E8), - ('PCM_explode', 0x001F78A8), - ('PCM_splash', 0x00209A38) -] - -so_file_sha256 = '157af341d13a54cc935bbe24c5e1cf3d02d7e40ec20f9859b9853c2e996ebd81' - -header_1 = '// Minecraft Pocket Edition Reverse Engineering Project\n// Copyright (C) 2011 Mojang Specifications.\n' -header_2 = '// Data extracted from libminecraftpe.so (Minecraft PE v0.1.3.apk).\n\n' - -def read_int_from_bytes(arr_bytes, offset): - return arr_bytes[offset] | arr_bytes[offset + 1] << 8 | arr_bytes[offset + 2] << 16 | arr_bytes[offset + 3] << 24 - -def read_short_from_bytes(arr_bytes, offset): - return arr_bytes[offset] | arr_bytes[offset + 1] << 8 - -def format_hex_short(short): - return "{:04x}".format(short) - -def main(): - if len(sys.argv) < 2: - print('Usage:', sys.argv[0], '[libminecraftpe.so] (output dir)') - return - - output_dir = '' - - if len(sys.argv) > 2: - output_dir = sys.argv[2] - - if len(output_dir) == 0: - output_dir = './sound_data/' - - # append a slash to the file name, if it doesn't have one already - if output_dir[len(output_dir) - 1] != '/': - output_dir += '/' - - if not os.path.exists(output_dir): - os.makedirs(output_dir) - - bytes = [] - - # open the file - try: - with open(sys.argv[1],mode='rb') as sofile: - # read all bytes - bytes = sofile.read() - - except FileNotFoundError: - print('ERROR: The file', sys.argv[1], 'was not found.') - return - - # first off, hash the file to ensure it matches - src_sha256 = hashlib.sha256(bytes).hexdigest() - - print('* Source file hash: ', src_sha256) - - if src_sha256 != so_file_sha256: - print('ERROR: Source file does not match the Source APK\'s SO file. Please extract one from the aforementioned APK. See the readme for more details.') - return - - print('* Extracting PCM data...') - - include_all_str = '#pragma once\n\n' + header_1 - include_all_str+= '\n// @NOTE: This is meant to be included in exactly 1 compile object/source file.\n\n' + header_2 - - for item in pcm_addresses: - include_all_str += '#include "' + item[0] + '.h"\n' - ostr = header_1 + header_2 - - channels = read_int_from_bytes(bytes, item[1] + 0) - data_length = read_int_from_bytes(bytes, item[1] + 12) - - # @NOTE: All the PCM sound data is in `.data`. So there's no consts to be found. - - ostr += 'PCMSoundHeader '+item[0]+' =\n{\n' - ostr += '\t' + str(channels) + ',\n' - ostr += '\t' + str(read_int_from_bytes(bytes, item[1] + 4)) + ',\n' - ostr += '\t' + str(read_int_from_bytes(bytes, item[1] + 8)) + ',\n' - ostr += '\t' + str(data_length) - ostr += '\n};\n\n' - - data_length *= channels - - ostr += 'uint16_t '+item[0]+'_data['+str(data_length)+'] =\n{' - - for i in merange(data_length): - if i % 16 == 0: - ostr += '\n\t' - ostr += '0x'+format_hex_short(read_short_from_bytes(bytes, item[1] + 16 + i * 2)) + ',' - - ostr += '\n};\n' - - filename = output_dir + item[0] + '.h' - with open(filename, 'w') as of: - of.write(ostr) - - include_all_str += '\n\n' - - for item in pcm_addresses: - include_all_str += 'SoundDesc SA_' + item[0][4:] + '(' + item[0] + ', ' + item[0] + '_data);\n' - - with open(output_dir + 'sounds.h', 'w') as of: - of.write(include_all_str) - - pass - - +# Main Function +def main(file): + # Output Directory + out = os.path.dirname(os.path.abspath(__file__)) + out = os.path.join(out, '..', 'game', 'assets', 'sound') + os.makedirs(out, exist_ok=True) + # Extract Sound Data + elf = lief.parse(file) + prefix = 'PCM_' + for sym in elf.dynamic_symbols: + if sym.name.startswith('PCM_'): + # Found Sound Symbol, Extract It + name = sym.name[len(prefix):] + print(f'Extracting Sound {name}...') + path = os.path.join(out, name + '.pcm') + data = bytes(elf.get_content_from_virtual_address(sym.value, sym.size)) + with open(path, 'wb') as file: + file.write(data) + +# Entrypoint if __name__ == '__main__': - main() + # Check Arguments + if len(sys.argv) < 2: + print(f'USAGE: {__file__} libminecraftpe.so') + sys.exit() + # Run + main(sys.argv[1]) + # Done + print('Done!') diff --git a/tools/setup_venv.py b/tools/setup_venv.py new file mode 100644 index 000000000..ae6010bfc --- /dev/null +++ b/tools/setup_venv.py @@ -0,0 +1,44 @@ +import os +import venv +import sys +import subprocess +import platform + +# Find Binary Directory +bin_dir = 'bin' +if platform.system() == 'Windows': + bin_dir = 'Scripts' + +# Paths +path = os.path.dirname(os.path.abspath(__file__)) +path = os.path.join(path, 'venv') +valid_file = os.path.join(path, '.valid') + +# Open In Virtualenv +def restart_in_venv(path): + print('Activating Virtualenv...') + py = os.path.basename(sys.executable) + py = os.path.join(path, bin_dir, py) + os.execv(py, [py] + sys.argv) + +# Create Virtualenv +def create_venv(path): + # Check If It Already Exists + if os.path.exists(valid_file): + return + # Build Environment + print('Creating Virtualenv...') + builder = venv.EnvBuilder(clear=True, symlinks=False, with_pip=True) + builder.create(path) + # Install LIEF + pip = 'pip' + if platform.system() == 'Windows': + pip += '.exe' + subprocess.run([os.path.join(path, bin_dir, pip), 'install', 'lief']) + # Mark As Created + open(valid_file, 'wb').close() + +# Run +if not sys.executable.startswith(path): + create_venv(path) + restart_in_venv(path)