diff --git a/BeefLibs/SDL2/BeefProj.toml b/BeefLibs/SDL2/BeefProj.toml index 3fee7db7..5c7de309 100644 --- a/BeefLibs/SDL2/BeefProj.toml +++ b/BeefLibs/SDL2/BeefProj.toml @@ -9,12 +9,15 @@ StartupObject = "Program" LibPaths = ["$(ProjectDir)/dist/SDL2.lib", "$(ProjectDir)/dist/SDL2_ttf.lib", "$(ProjectDir)/dist/SDL2_image.lib", "$(ProjectDir)/dist/SDL2_mixer.lib"] PostBuildCmds = ["CopyToDependents(\"$(ProjectDir)/dist/*.dll\")"] +[Configs.Debug.Linux64] +OtherLinkFlags = "$(LinkFlags) -lSDL2 -lSDL2_ttf -lSDL2_image -lSDL2_mixer" + +[Configs.Debug.wasm32] +PreprocessorMacros = ["DEBUG", "NOTTF"] + [Configs.Release.Win64] LibPaths = ["$(ProjectDir)/dist/SDL2.lib", "$(ProjectDir)/dist/SDL2_ttf.lib", "$(ProjectDir)/dist/SDL2_image.lib", "$(ProjectDir)/dist/SDL2_mixer.lib"] PostBuildCmds = ["CopyToDependents(\"$(ProjectDir)/dist/*.dll\")"] -[Configs.Debug.Linux64] -OtherLinkFlags = "$(LinkFlags) -lSDL2 -lSDL2_ttf -lSDL2_image -lSDL2_mixer" - [Configs.Release.Linux64] OtherLinkFlags = "$(LinkFlags) -lSDL2 -lSDL2_ttf -lSDL2_image -lSDL2_mixer" diff --git a/BeefLibs/SDL2/src/BitmapFont.bf b/BeefLibs/SDL2/src/BitmapFont.bf new file mode 100644 index 00000000..6a80e1bb --- /dev/null +++ b/BeefLibs/SDL2/src/BitmapFont.bf @@ -0,0 +1,32 @@ +using System.Collections; + +namespace SDL2 +{ + class BitmapFont + { + public class Page + { + public Image mImage ~ delete _; + } + + public struct CharData + { + public int32 mX; + public int32 mY; + public int32 mWidth; + public int32 mHeight; + public int32 mXOfs; + public int32 mYOfs; + public int32 mXAdvance; + public int32 mPage; + } + + public List mPages = new .() ~ DeleteContainerAndItems!(_); + public List mCharData = new .() ~ delete _; + + public this() + { + + } + } +} \ No newline at end of file diff --git a/BeefLibs/SDL2/src/SDL2_image.bf b/BeefLibs/SDL2/src/SDL2_image.bf index 9572c641..5fb6b102 100644 --- a/BeefLibs/SDL2/src/SDL2_image.bf +++ b/BeefLibs/SDL2/src/SDL2_image.bf @@ -79,7 +79,7 @@ namespace SDL2 ); [LinkName("IMG_LoadTexture")] - private static extern SDL.Texture* LoadTexture( + public static extern SDL.Texture* LoadTexture( SDL.Renderer* renderer, char8* file ); diff --git a/BeefLibs/SDL2/src/SDLApp.bf b/BeefLibs/SDL2/src/SDLApp.bf index 6fc9f5f4..06e7799f 100644 --- a/BeefLibs/SDL2/src/SDLApp.bf +++ b/BeefLibs/SDL2/src/SDLApp.bf @@ -10,6 +10,8 @@ namespace SDL2 { public SDL.Surface* mSurface; public SDL.Texture* mTexture; + public int32 mWidth; + public int32 mHeight; public ~this() { @@ -26,7 +28,25 @@ namespace SDL2 return .Err; mSurface = origSurface; - mTexture = SDL.CreateTextureFromSurface(gApp.mRenderer, mSurface); + + SDL.Rect rect = .(0, 0, mSurface.w, mSurface.h); + mTexture = SDL.CreateTexture(gApp.mRenderer, SDL.PIXELFORMAT_ABGR8888, (.)SDL.TextureAccess.Static, mSurface.w, mSurface.h); + + uint32* data = new uint32[mSurface.w*mSurface.h]*; + defer delete data; + + int res = SDL.ConvertPixels(mSurface.w, mSurface.h, mSurface.format.format, mSurface.pixels, mSurface.pitch, SDL.PIXELFORMAT_ABGR8888, data, mSurface.w * 4); + if (res == -1) + { + for (int y = 0; y < mSurface.h; y++) + Internal.MemCpy(data + y*mSurface.w, (uint8*)mSurface.pixels + y*mSurface.pitch, mSurface.w*4); + } + + SDL.UpdateTexture(mTexture, &rect, data, mSurface.w * 4); + SDL.SetTextureBlendMode(mTexture, .Blend); + + mWidth = mSurface.w; + mHeight = mSurface.h; return .Ok; } @@ -54,6 +74,7 @@ namespace SDL2 class Font { public SDLTTF.Font* mFont; + public BitmapFont mBMFont; public this() { @@ -64,10 +85,90 @@ namespace SDL2 { if (mFont != null) SDLTTF.CloseFont(mFont); + delete mBMFont; } public Result Load(StringView fileName, int32 pointSize) { + if (fileName.EndsWith(".fnt")) + { + mBMFont = new BitmapFont(); + + var contents = File.ReadAllText(fileName, .. scope .()); + contents.Replace("\r", ""); + for (var line in contents.Split('\n', .RemoveEmptyEntries)) + { + bool CheckVal(StringView text, StringView key, ref int32 val) + { + if (!text.StartsWith(key)) + return false; + if (text[key.Length] != '=') + return false; + val = int32.Parse(text.Substring(key.Length + 1)); + return true; + } + + bool CheckVal(StringView text, String key, String val) + { + if (!text.StartsWith(key)) + return false; + if (text[key.Length] != '=') + return false; + StringView sv = text.Substring(key.Length + 1); + if (sv.StartsWith('"')) + sv.RemoveFromStart(1); + if (sv.EndsWith('"')) + sv.RemoveFromEnd(1); + val.Append(sv); + return true; + } + + if (line.StartsWith("page")) + { + String imageFileName = scope .(); + + for (var text in line.Split(' ', .RemoveEmptyEntries)) + CheckVal(text, "file", imageFileName); + + var dir = Path.GetDirectoryPath(fileName, .. scope .()); + var imagePath = scope $"{dir}/{imageFileName}"; + + BitmapFont.Page page = new BitmapFont.Page(); + page.mImage = new Image()..Load(imagePath); + mBMFont.mPages.Add(page); + } + + if (line.StartsWith("char")) + { + BitmapFont.CharData* charData = null; + + for (var text in line.Split(' ', .RemoveEmptyEntries)) + { + int32 id = 0; + if (CheckVal(text, "id", ref id)) + { + while (id >= mBMFont.mCharData.Count) + mBMFont.mCharData.Add(default); + charData = &mBMFont.mCharData[id]; + } + + if (charData != null) + { + CheckVal(text, "x", ref charData.mX); + CheckVal(text, "y", ref charData.mY); + CheckVal(text, "width", ref charData.mWidth); + CheckVal(text, "height", ref charData.mHeight); + CheckVal(text, "xoffset", ref charData.mXOfs); + CheckVal(text, "yoffset", ref charData.mYOfs); + CheckVal(text, "xadvance", ref charData.mXAdvance); + CheckVal(text, "page", ref charData.mPage); + } + } + } + } + return .Ok; + } + mFont = SDLTTF.OpenFont(fileName.ToScopeCStr!(), pointSize); if (mFont == null) return .Err; @@ -86,7 +187,10 @@ namespace SDL2 public int32 mHeight = 768; public bool* mKeyboardState; public bool mHasAudio; + public bool mDidInit; + private Stopwatch mFPSStopwatch = new .() ~ delete _; + private int32 mFPSCount; private Stopwatch mStopwatch = new .() ~ delete _; private int mCurPhysTickCount = 0; @@ -103,8 +207,17 @@ namespace SDL2 SDL.DestroyWindow(mWindow); } - public void Init() + public void PreInit() { +#if BF_PLATFORM_WASM + emscripten_set_main_loop(=> EmscriptenMainLoop, 0, 1); +#endif + } + + public virtual void Init() + { + mDidInit = true; + String exePath = scope .(); Environment.GetExecutableFilePath(exePath); String exeDir = scope .(); @@ -120,12 +233,16 @@ namespace SDL2 SDL.EventState(.JoyDeviceAdded, .Disable); SDL.EventState(.JoyDeviceRemoved, .Disable); - mWindow = SDL.CreateWindow(mTitle, .Undefined, .Undefined, mWidth, mHeight, .Hidden); // Initially hide window + //mWindow = SDL.CreateWindow(mTitle, .Undefined, .Undefined, mWidth, mHeight, .Hidden); // Initially hide window + mWindow = SDL.CreateWindow(mTitle, .Undefined, .Undefined, mWidth, mHeight, .Shown); // Initially hide window + mRenderer = SDL.CreateRenderer(mWindow, -1, .Accelerated); mScreen = SDL.GetWindowSurface(mWindow); - SDLImage.Init(.PNG | .JPG); + SDLImage.Init(.PNG); mHasAudio = SDLMixer.OpenAudio(44100, SDLMixer.MIX_DEFAULT_FORMAT, 2, 4096) >= 0; + SDL.SetRenderDrawBlendMode(mRenderer, .Blend); + SDLTTF.Init(); } @@ -176,19 +293,77 @@ namespace SDL2 public void Draw(Image image, float x, float y) { - SDL.Rect srcRect = .(0, 0, image.mSurface.w, image.mSurface.h); - SDL.Rect destRect = .((int32)x, (int32)y, image.mSurface.w, image.mSurface.h); + SDL.Rect srcRect = .(0, 0, image.mWidth, image.mHeight); + SDL.Rect destRect = .((int32)x, (int32)y, image.mWidth, image.mHeight); SDL.RenderCopy(mRenderer, image.mTexture, &srcRect, &destRect); } public void Draw(Image image, float x, float y, float rot, float centerX, float centerY) { - SDL.Rect srcRect = .(0, 0, image.mSurface.w, image.mSurface.h); - SDL.Rect destRect = .((int32)x, (int32)y, image.mSurface.w, image.mSurface.h); + SDL.Rect srcRect = .(0, 0, image.mWidth, image.mHeight); + SDL.Rect destRect = .((int32)x, (int32)y, image.mWidth, image.mHeight); SDL.Point centerPoint = .((.)centerX, (.)centerY); SDL.RenderCopyEx(mRenderer, image.mTexture, &srcRect, &destRect, rot, ¢erPoint, .None); } + public void DrawString(Font font, float x, float y, String str, SDL.Color color, bool centerX = false) + { + var x; + if (font.mBMFont != null) + { + for (var page in font.mBMFont.mPages) + SDL.SetTextureColorMod(page.mImage.mTexture, color.r, color.g, color.b); + + var drawX = x; + var drawY = y; + + if (centerX) + { + float width = 0; + + for (var c in str.RawChars) + { + if ((int32)c >= font.mBMFont.mCharData.Count) + continue; + var charData = ref font.mBMFont.mCharData[(int32)c]; + width += charData.mXAdvance; + } + + drawX -= width / 2; + } + + for (var c in str.RawChars) + { + if ((int32)c >= font.mBMFont.mCharData.Count) + continue; + var charData = ref font.mBMFont.mCharData[(int32)c]; + + SDL.Rect srcRect = .(charData.mX, charData.mY, charData.mWidth, charData.mHeight); + SDL.Rect destRect = .((int32)drawX + charData.mXOfs, (int32)drawY + charData.mYOfs, charData.mWidth, charData.mHeight); + SDL.RenderCopy(mRenderer, font.mBMFont.mPages[charData.mPage].mImage.mTexture, &srcRect, &destRect); + + drawX += charData.mXAdvance; + } + } + else + { +#if !NOTTF + SDL.SetRenderDrawColor(mRenderer, 255, 255, 255, 255); + let surface = SDLTTF.RenderUTF8_Blended(font.mFont, str, color); + let texture = SDL.CreateTextureFromSurface(mRenderer, surface); + SDL.Rect srcRect = .(0, 0, surface.w, surface.h); + + if (centerX) + x -= surface.w / 2; + + SDL.Rect destRect = .((int32)x, (int32)y, surface.w, surface.h); + SDL.RenderCopy(mRenderer, texture, &srcRect, &destRect); + SDL.FreeSurface(surface); + SDL.DestroyTexture(texture); +#endif + } + } + public void PlaySound(Sound sound, float volume = 1.0f, float pan = 0.5f) { if (sound == null) @@ -214,8 +389,19 @@ namespace SDL2 [CLink, CallingConvention(.Stdcall)] private static extern void emscripten_set_main_loop(em_callback_func func, int32 fps, int32 simulateInfinteLoop); + [CLink, CallingConvention(.Stdcall)] + private static extern int32 emscripten_set_main_loop_timing(int32 mode, int32 value); + + [CLink, CallingConvention(.Stdcall)] + private static extern double emscripten_get_now(); + private static void EmscriptenMainLoop() { + if (!gApp.mDidInit) + { + gApp.Init(); + gApp.mStopwatch.Start(); + } gApp.RunOneFrame(); } #endif @@ -225,15 +411,25 @@ namespace SDL2 mStopwatch.Start(); #if BF_PLATFORM_WASM - emscripten_set_main_loop(=> EmscriptenMainLoop, -1, 1); + emscripten_set_main_loop(=> EmscriptenMainLoop, 0, 1); #else - while (true) - RunOneFrame(); + while (RunOneFrame()) {} + #endif } - private void RunOneFrame() + private bool RunOneFrame() { + if (!mFPSStopwatch.IsRunning) + mFPSStopwatch.Start(); + mFPSCount++; + if (mFPSStopwatch.ElapsedMilliseconds > 1000) + { + //Debug.WriteLine($"FPS: {mFPSCount} @ {mStopwatch.Elapsed} now: {emscripten_get_now()}"); + mFPSCount = 0; + mFPSStopwatch.Restart(); + } + int32 waitTime = 1; SDL.Event event; @@ -242,7 +438,7 @@ namespace SDL2 switch (event.type) { case .Quit: - return; + return false; case .KeyDown: KeyDown(event.key); case .KeyUp: @@ -292,6 +488,7 @@ namespace SDL2 } mCurPhysTickCount = newPhysTickCount; + return true; } } diff --git a/IDE/src/BuildContext.bf b/IDE/src/BuildContext.bf index da4fcf96..1e9c3635 100644 --- a/IDE/src/BuildContext.bf +++ b/IDE/src/BuildContext.bf @@ -589,9 +589,11 @@ namespace IDE if (isExe || isDynLib) { + var actualTargetPath = Path.GetActualPathName(targetPath, .. scope .()); + String linkLine = scope String(); linkLine.Append("-o "); - IDEUtils.AppendWithOptionalQuotes(linkLine, targetPath); + IDEUtils.AppendWithOptionalQuotes(linkLine, actualTargetPath); linkLine.Append(" "); linkLine.Append(objectsArg); @@ -667,7 +669,8 @@ namespace IDE linkLine.Replace('\\', '/'); - var runCmd = gApp.QueueRun(compilerExePath, linkLine, project.mProjectDir, .UTF8); + var targetDir = Path.GetDirectoryPath(actualTargetPath, .. scope .()); + var runCmd = gApp.QueueRun(compilerExePath, linkLine, targetDir, .UTF8); runCmd.mReference = new .(project.mProjectName); runCmd.mOnlyIfNotFailed = true; var tagetCompletedCmd = new IDEApp.TargetCompletedCmd(project); diff --git a/IDE/src/Project.bf b/IDE/src/Project.bf index ed6c5e02..9ef7c60f 100644 --- a/IDE/src/Project.bf +++ b/IDE/src/Project.bf @@ -1061,16 +1061,16 @@ namespace IDE public class WasmOptions { [Reflect] - public bool mEnableThreads = true; + public bool mEnableThreads = false; public void Deserialize(StructuredData data) { - mEnableThreads = data.GetBool("EnableThreads", true); + mEnableThreads = data.GetBool("EnableThreads", false); } public void Serialize(StructuredData data) { - data.ConditionalAdd("EnableThreads", mEnableThreads, true); + data.ConditionalAdd("EnableThreads", mEnableThreads, false); } }