361 lines
10 KiB
C++
361 lines
10 KiB
C++
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#include <Jolt/Jolt.h>
|
|
#include <Jolt/ConfigurationString.h>
|
|
#include <Jolt/Core/FPException.h>
|
|
#include <Jolt/Core/Factory.h>
|
|
#include <Jolt/RegisterTypes.h>
|
|
#ifdef JPH_PLATFORM_WINDOWS
|
|
#include <crtdbg.h>
|
|
#endif // JPH_PLATFORM_WINDOWS
|
|
#ifdef JPH_PLATFORM_ANDROID
|
|
#include <Jolt/Core/Color.h>
|
|
#include <android/log.h>
|
|
#include <android_native_app_glue.h>
|
|
#endif // JPH_PLATFORM_ANDROID
|
|
|
|
JPH_SUPPRESS_WARNINGS_STD_BEGIN
|
|
#include <cstdarg>
|
|
JPH_SUPPRESS_WARNINGS_STD_END
|
|
|
|
using namespace JPH;
|
|
|
|
// Emit everything needed for the main function
|
|
#define DOCTEST_CONFIG_IMPLEMENT
|
|
#define DOCTEST_CONFIG_NO_WINDOWS_SEH
|
|
|
|
JPH_SUPPRESS_WARNINGS_STD_BEGIN
|
|
JPH_CLANG_16_PLUS_SUPPRESS_WARNING("-Wunsafe-buffer-usage")
|
|
JPH_MSVC2026_PLUS_SUPPRESS_WARNING(4865) // wingdi.h(2806,1): '<unnamed-enum-DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER>': the underlying type will change from 'int' to '__int64' when '/Zc:enumTypes' is specified on the command line
|
|
#include "doctest.h"
|
|
JPH_SUPPRESS_WARNINGS_STD_END
|
|
|
|
using namespace doctest;
|
|
|
|
// Disable common warnings triggered by Jolt
|
|
JPH_SUPPRESS_WARNINGS
|
|
|
|
// Callback for traces
|
|
static void TraceImpl(const char *inFMT, ...)
|
|
{
|
|
// Format the message
|
|
va_list list;
|
|
va_start(list, inFMT);
|
|
char buffer[1024];
|
|
vsnprintf(buffer, sizeof(buffer), inFMT, list);
|
|
va_end(list);
|
|
|
|
// Forward to doctest
|
|
MESSAGE(buffer);
|
|
}
|
|
|
|
#ifdef JPH_ENABLE_ASSERTS
|
|
|
|
// Callback for asserts
|
|
static bool AssertFailedImpl(const char *inExpression, const char *inMessage, const char *inFile, uint inLine)
|
|
{
|
|
// Format message
|
|
char buffer[1024];
|
|
snprintf(buffer, sizeof(buffer), "%s:%u: (%s) %s", inFile, inLine, inExpression, inMessage != nullptr? inMessage : "");
|
|
|
|
// Forward to doctest
|
|
FAIL_CHECK(buffer);
|
|
|
|
// No breakpoint
|
|
return false;
|
|
}
|
|
|
|
#endif // JPH_ENABLE_ASSERTS
|
|
|
|
#ifdef JPH_PLATFORM_WINDOWS_UWP
|
|
|
|
JPH_SUPPRESS_WARNING_PUSH
|
|
JPH_MSVC_SUPPRESS_WARNING(4265) // warning C4265: 'winrt::impl::implements_delegate<winrt::Windows::UI::Core::DispatchedHandler,H>': class has virtual functions, but its non-trivial destructor is not virtual; instances of this class may not be destructed correctly
|
|
JPH_MSVC_SUPPRESS_WARNING(4668) // warning C4668: '_MANAGED' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif'
|
|
JPH_MSVC_SUPPRESS_WARNING(4946) // warning C4946: reinterpret_cast used between related classes: 'winrt::impl::abi<winrt::Windows::ApplicationModel::Core::IFrameworkViewSource,void>::type' and 'winrt::impl::abi<winrt::Windows::Foundation::IUnknown,void>::type'
|
|
JPH_MSVC_SUPPRESS_WARNING(5039) // winbase.h(13179): warning C5039: 'TpSetCallbackCleanupGroup': pointer or reference to potentially throwing function passed to 'extern "C"' function under -EHc. Undefined behavior may occur if this function throws an exception.
|
|
JPH_MSVC_SUPPRESS_WARNING(5204) // warning C5204: 'winrt::impl::produce_base<D,winrt::Windows::ApplicationModel::Core::IFrameworkViewSource,void>': class has virtual functions, but its trivial destructor is not virtual; instances of objects derived from this class may not be destructed correctly
|
|
JPH_MSVC_SUPPRESS_WARNING(5246) // warning C5246: '_Elems': the initialization of a subobject should be wrapped in braces
|
|
#include <winrt/Windows.Foundation.h>
|
|
#include <winrt/Windows.Foundation.Collections.h>
|
|
#include <winrt/Windows.ApplicationModel.Core.h>
|
|
#include <winrt/Windows.UI.Core.h>
|
|
#include <winrt/Windows.UI.Composition.h>
|
|
#include <winrt/Windows.UI.Input.h>
|
|
JPH_SUPPRESS_WARNING_POP
|
|
|
|
using namespace winrt;
|
|
using namespace Windows;
|
|
using namespace Windows::ApplicationModel::Core;
|
|
using namespace Windows::UI;
|
|
using namespace Windows::UI::Core;
|
|
using namespace Windows::UI::Composition;
|
|
|
|
struct App : implements<App, IFrameworkViewSource, IFrameworkView>
|
|
{
|
|
CompositionTarget mTarget { nullptr };
|
|
|
|
IFrameworkView CreateView()
|
|
{
|
|
return *this;
|
|
}
|
|
|
|
void Initialize(CoreApplicationView const&)
|
|
{
|
|
}
|
|
|
|
void Load(hstring const&)
|
|
{
|
|
}
|
|
|
|
void Uninitialize()
|
|
{
|
|
}
|
|
|
|
void Run()
|
|
{
|
|
CoreWindow window = CoreWindow::GetForCurrentThread();
|
|
window.Activate();
|
|
|
|
CoreDispatcher dispatcher = window.Dispatcher();
|
|
dispatcher.ProcessEvents(CoreProcessEventsOption::ProcessUntilQuit);
|
|
}
|
|
|
|
void SetWindow(CoreWindow const& inWindow)
|
|
{
|
|
// Register allocation hook
|
|
RegisterDefaultAllocator();
|
|
|
|
// Install callbacks
|
|
Trace = TraceImpl;
|
|
JPH_IF_ENABLE_ASSERTS(AssertFailed = AssertFailedImpl;)
|
|
|
|
#ifdef _DEBUG
|
|
// Enable leak detection
|
|
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
|
#endif
|
|
|
|
// Enable floating point exceptions
|
|
FPExceptionsEnable enable_exceptions;
|
|
JPH_UNUSED(enable_exceptions);
|
|
|
|
// Create a factory
|
|
Factory::sInstance = new Factory();
|
|
|
|
// Register physics types
|
|
RegisterTypes();
|
|
|
|
// Run the tests
|
|
int rv = Context().run();
|
|
|
|
// Unregisters all types with the factory and cleans up the default material
|
|
UnregisterTypes();
|
|
|
|
// Destroy the factory
|
|
delete Factory::sInstance;
|
|
Factory::sInstance = nullptr;
|
|
|
|
// Color the screen according to the result
|
|
Compositor compositor;
|
|
ContainerVisual root = compositor.CreateContainerVisual();
|
|
mTarget = compositor.CreateTargetForCurrentView();
|
|
mTarget.Root(root);
|
|
SpriteVisual visual = compositor.CreateSpriteVisual();
|
|
visual.Brush(compositor.CreateColorBrush(rv != 0 ? Windows::UI::Color { 0xff, 0xff, 0x00, 0x00 } : Windows::UI::Color { 0xff, 0x00, 0xff, 0x00 }));
|
|
visual.Size({ inWindow.Bounds().Width, inWindow.Bounds().Height });
|
|
visual.Offset({ 0, 0, 0, });
|
|
root.Children().InsertAtTop(visual);
|
|
}
|
|
};
|
|
|
|
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
|
|
{
|
|
CoreApplication::Run(make<App>());
|
|
}
|
|
|
|
#elif !defined(JPH_PLATFORM_ANDROID)
|
|
|
|
// Generic entry point
|
|
int main(int argc, char** argv)
|
|
{
|
|
// Show used instruction sets
|
|
std::cout << GetConfigurationString() << std::endl;
|
|
|
|
// Register allocation hook
|
|
RegisterDefaultAllocator();
|
|
|
|
// Install callbacks
|
|
Trace = TraceImpl;
|
|
JPH_IF_ENABLE_ASSERTS(AssertFailed = AssertFailedImpl;)
|
|
|
|
#if defined(JPH_PLATFORM_WINDOWS) && defined(_DEBUG)
|
|
// Enable leak detection
|
|
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
|
#endif
|
|
|
|
// Enable floating point exceptions
|
|
FPExceptionsEnable enable_exceptions;
|
|
JPH_UNUSED(enable_exceptions);
|
|
|
|
// Create a factory
|
|
Factory::sInstance = new Factory();
|
|
|
|
// Register physics types
|
|
RegisterTypes();
|
|
|
|
int rv = Context(argc, argv).run();
|
|
|
|
// Unregisters all types with the factory and cleans up the default material
|
|
UnregisterTypes();
|
|
|
|
// Destroy the factory
|
|
delete Factory::sInstance;
|
|
Factory::sInstance = nullptr;
|
|
|
|
return rv;
|
|
}
|
|
|
|
#else // !JPH_PLATFORM_ANDROID
|
|
|
|
// Reporter that writes logs to the Android log
|
|
struct LogReporter : public ConsoleReporter
|
|
{
|
|
LogReporter(const ContextOptions &inOptions) :
|
|
ConsoleReporter(inOptions, mStream)
|
|
{
|
|
}
|
|
|
|
#define REPORTER_OVERRIDE(func, type, arg) \
|
|
void func(type arg) override \
|
|
{ \
|
|
ConsoleReporter::func(arg); \
|
|
std::string str = mStream.str(); \
|
|
if (!str.empty()) \
|
|
__android_log_write(ANDROID_LOG_INFO, "Jolt", str.c_str()); \
|
|
mStream.str(""); \
|
|
}
|
|
|
|
REPORTER_OVERRIDE(test_run_start, DOCTEST_EMPTY, DOCTEST_EMPTY)
|
|
REPORTER_OVERRIDE(test_run_end, const TestRunStats &, in)
|
|
REPORTER_OVERRIDE(test_case_start, const TestCaseData &, in)
|
|
REPORTER_OVERRIDE(test_case_reenter, const TestCaseData &, in)
|
|
REPORTER_OVERRIDE(test_case_end, const CurrentTestCaseStats &, in)
|
|
REPORTER_OVERRIDE(test_case_exception, const TestCaseException &, in)
|
|
REPORTER_OVERRIDE(subcase_start, const SubcaseSignature &, in)
|
|
REPORTER_OVERRIDE(subcase_end, DOCTEST_EMPTY, DOCTEST_EMPTY)
|
|
REPORTER_OVERRIDE(log_assert, const AssertData &, in)
|
|
REPORTER_OVERRIDE(log_message, const MessageData &, in)
|
|
REPORTER_OVERRIDE(test_case_skipped, const TestCaseData &, in)
|
|
|
|
private:
|
|
thread_local static std::ostringstream mStream;
|
|
};
|
|
|
|
thread_local std::ostringstream LogReporter::mStream;
|
|
|
|
DOCTEST_REGISTER_REPORTER("android_log", 0, LogReporter);
|
|
|
|
void AndroidInitialize(android_app *inApp)
|
|
{
|
|
// Log configuration
|
|
__android_log_write(ANDROID_LOG_INFO, "Jolt", GetConfigurationString());
|
|
|
|
// Register allocation hook
|
|
RegisterDefaultAllocator();
|
|
|
|
// Install callbacks
|
|
Trace = TraceImpl;
|
|
JPH_IF_ENABLE_ASSERTS(AssertFailed = AssertFailedImpl;)
|
|
|
|
// Enable floating point exceptions
|
|
FPExceptionsEnable enable_exceptions;
|
|
JPH_UNUSED(enable_exceptions);
|
|
|
|
// Create a factory
|
|
Factory::sInstance = new Factory();
|
|
|
|
// Register physics types
|
|
RegisterTypes();
|
|
|
|
// Run all tests
|
|
Context context;
|
|
context.addFilter("reporters", "android_log");
|
|
int return_value = context.run();
|
|
|
|
// Color the screen according to the test result
|
|
JPH::Color color = return_value == 0? JPH::Color::sGreen : JPH::Color::sRed;
|
|
ANativeWindow_acquire(inApp->window);
|
|
ANativeWindow_Buffer buffer;
|
|
ARect bounds;
|
|
ANativeWindow_lock(inApp->window, &buffer, &bounds);
|
|
switch (buffer.format)
|
|
{
|
|
case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
|
|
case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
|
|
{
|
|
uint32 color_u32 = color.GetUInt32();
|
|
for (int y = 0; y < buffer.height; ++y)
|
|
{
|
|
uint32 *dest = (uint32 *)((uint8 *)buffer.bits + y * buffer.stride * sizeof(uint32));
|
|
for (int x = 0; x < buffer.width; ++x)
|
|
*dest++ = color_u32;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
|
|
{
|
|
uint16 color_u16 = (color.b >> 3) + ((color.g >> 2) << 5) + ((color.r >> 3) << 11);
|
|
for (int y = 0; y < buffer.height; ++y)
|
|
{
|
|
uint16 *dest = (uint16 *) ((uint8 *) buffer.bits + y * buffer.stride * sizeof(uint16));
|
|
for (int x = 0; x < buffer.width; ++x)
|
|
*dest++ = color_u16;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
// TODO implement
|
|
break;
|
|
}
|
|
ANativeWindow_unlockAndPost(inApp->window);
|
|
ANativeWindow_release(inApp->window);
|
|
|
|
// Unregisters all types with the factory and cleans up the default material
|
|
UnregisterTypes();
|
|
|
|
// Destroy the factory
|
|
delete Factory::sInstance;
|
|
Factory::sInstance = nullptr;
|
|
}
|
|
|
|
// Handle callback from Android
|
|
void AndroidHandleCommand(android_app *inApp, int32_t inCmd)
|
|
{
|
|
switch (inCmd)
|
|
{
|
|
case APP_CMD_INIT_WINDOW:
|
|
AndroidInitialize(inApp);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Main entry point for android
|
|
void android_main(struct android_app *ioApp)
|
|
{
|
|
ioApp->onAppCmd = AndroidHandleCommand;
|
|
|
|
int events;
|
|
android_poll_source *source;
|
|
do
|
|
{
|
|
if (ALooper_pollAll(1, nullptr, &events, (void **)&source) >= 0 && source != nullptr)
|
|
source->process(ioApp, source);
|
|
} while (ioApp->destroyRequested == 0);
|
|
}
|
|
|
|
#endif // JPH_PLATFORM_ANDROID
|